////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2001.
// -------------------------------------------------------------------------
//  File name:   EntityObject.cpp
//  Version:     v1.00
//  Created:     10/10/2001 by Timur.
//  Compilers:   Visual C++ 6.0
//  Description: StaticObject implementation.
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "EntityObject.h"
#include "EntityPanel.h"
#include "EntityLinksPanel.h"
#include "PanelTreeBrowser.h"
#include "PropertiesPanel.h"
#include "Viewport.h"
#include "LineGizmo.h"
#include "Group.h"

#include <I3DEngine.h>
#include <IAgent.h>
#include <IMovieSystem.h>
#include <IEntitySystem.h>
#include <ICryAnimation.h>
#include <IEntityRenderState.h>
#include "IEntityPoolManager.h"
#include "IIndexedMesh.h"

#include "EntityPrototype.h"
#include "Material/MaterialManager.h"

#include "StringDlg.h"
#include "GenericSelectItemDialog.h"

#include "HyperGraph/FlowGraphManager.h"
#include "HyperGraph/FlowGraph.h"
#include "HyperGraph/HyperGraphDialog.h"
#include "HyperGraph/FlowGraphSearchCtrl.h"
#include "HyperGraph/FlowGraphHelpers.h"
#include "TrackView/TrackViewDialog.h"

#include "BrushObject.h"
#include "GameEngine.h"

#include "Console/ConsoleSync.h"
#include "SegmentedWorld/SegmentedWorldManager.h"
#include "IAIObject.h"
#include "IAIActor.h"

#include "TrackView/TrackViewUndo.h"

#include "Viewport.h"
#include "ViewManager.h"
#include "Light.h"
#include "CustomColorDialog.h"

#include "../Controls/PropertyItem.h"
#include "../Controls/DynamicPopupMenu.h"
#include <HyperGraph/AnimationGraph2/AnimationGraphDialog_2.h>
#include "IGameFramework.h"
#include "IActorSystem.h"
#include "IAnimatedCharacter.h"

//////////////////////////////////////////////////////////////////////////
// CBase implementation.
//////////////////////////////////////////////////////////////////////////
IMPLEMENT_DYNCREATE(CEntityObject,CBaseObject)
IMPLEMENT_DYNCREATE(CSimpleEntity,CEntityObject)
IMPLEMENT_DYNCREATE(CGeomEntity,CEntityObject)

int CEntityObject::m_rollupId = 0;
CEntityPanel* CEntityObject::m_panel = 0;
float CEntityObject::m_helperScale = 1;

namespace
{
	int s_entityEventsPanelIndex = 0;
	CEntityEventsPanel* s_entityEventsPanel = 0;

	int s_entityLinksPanelIndex = 0;
	CEntityLinksPanel* s_entityLinksPanel = 0;

	int s_propertiesPanelIndex = 0;
	CPropertiesPanel* s_propertiesPanel = 0;

	int s_propertiesPanelIndex2 = 0;
	CPropertiesPanel* s_propertiesPanel2 = 0;

	// Prevent OnPropertyChange to be executed when loading many properties at one time.
	static bool s_ignorePropertiesUpdate = false;

	CPanelTreeBrowser* s_treePanel = NULL;
	int s_treePanelId = 0;

	std::map<EntityId,CEntityObject*> s_entityIdMap;
};

void CEntityObject::DeleteUIPanels()
{
	delete s_propertiesPanel;
	delete s_propertiesPanel2;
	delete s_treePanel;
}

//////////////////////////////////////////////////////////////////////////
CEntityObject::CEntityObject()
{
	m_entity = 0;
	m_bLoadFailed = false;

	m_pClass = 0;

	m_visualObject = 0;

	m_box.min.Set(0,0,0);
	m_box.max.Set(0,0,0);

	m_proximityRadius = 0;
	m_innerRadius = 0;
	m_outerRadius = 0;

	m_pAnimNode = 0;
	m_bDisplayBBox = true;
	m_bDisplaySolidBBox = false;
	m_bDisplayAbsoluteRadius = false;
	m_bIconOnTop = false;
	m_bDisplayArrow = false;
	m_entityId = 0;
	m_bVisible = true;
	m_bCalcPhysics = true;
	//m_staticEntity = false;
	m_pFlowGraph = 0;
	m_bLight = false;
	m_bBoxProjectedCM = false;
	m_fBoxWidth = 1;
	m_fBoxHeight = 1;
	m_fBoxLength = 1;

	m_bEntityXfromValid = false;

	UseMaterialLayersMask();

	SetColor( RGB(255,255,0) );
	
	// Init Variables.
	mv_castShadow = true;
	mv_outdoor			=	false;
  mv_recvWind = false;
	mv_bending = 0.0f;
	mv_renderNearest = false;
	mv_noDecals = false;

	mv_createdThroughPool = false;

	//mv_castLightmap = false;
	//mv_recvLightmap = false;
	mv_hiddenInGame = false;
	mv_globalInSW = false;
	mv_ratioLOD = 100;
	mv_ratioViewDist = 100;
	mv_ratioLOD.SetLimits( 0,255 );
	mv_ratioViewDist.SetLimits( 0,255 );

	m_physicsState = 0;

	m_bForceScale=false;
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::InitVariables()
{
	//AddVariable( mv_selfShadowing,"SelfShadowing",functor(*this,&CEntity::OnRenderFlagsChange) );
	AddVariable( mv_outdoor,"OutdoorOnly",_T("Outdoor Only"),functor(*this,&CEntityObject::OnEntityFlagsChange) );
	AddVariable( mv_castShadow,"CastShadow",_T("Cast Shadow"),functor(*this,&CEntityObject::OnEntityFlagsChange) );  
	//AddVariable( mv_recvShadow,"RecvShadow",_T("Recieve Shadow"),functor(*this,&CEntity::OnEntityFlagsChange) );
  //AddVariable( mv_motionBlurAmount,"MotionBlurMultiplier",_T("Motion blur multiplier"),functor(*this,&CEntityObject::OnRenderFlagsChange) );
  
	//AddVariable( mv_recvShadowMapsASMR,"RecvShadowMapsASMR",functor(*this,&CEntity::OnRenderFlagsChange) );
	//AddVariable( mv_castLightmap,"PreCalcShadows",_T("CastLightmap"),functor(*this,&CEntity::OnRenderFlagsChange) );
	//AddVariable( mv_recvLightmap,"ReceiveLightmap",functor(*this,&CEntity::OnRenderFlagsChange) );
	AddVariable( mv_ratioLOD,"LodRatio",functor(*this,&CEntityObject::OnRenderFlagsChange) );
	AddVariable( mv_ratioViewDist,"ViewDistRatio",functor(*this,&CEntityObject::OnRenderFlagsChange) );
	AddVariable( mv_globalInSW, "GlobalInSegmentedWorld" );
	mv_globalInSW.SetDescription("Specify if the entity needs to be loaded before game starts");
	AddVariable( mv_hiddenInGame,"HiddenInGame" );
	AddVariable( mv_recvWind,"RecvWind", _T("Receive Wind"),functor(*this,&CEntityObject::OnEntityFlagsChange) );
	AddVariable( mv_bending,"Bending", _T("Bending multiplier"),functor(*this,&CEntityObject::OnEntityFlagsChange) );

	// [artemk]: Add RenderNearest entity param because of animator request.
	// This will cause that slot zero is rendered with ENTITY_SLOT_RENDER_NEAREST flag raised.
	// Used mostly in TrackView editor.
	AddVariable( mv_renderNearest,"RenderNearest", functor(*this,&CEntityObject::OnRenderFlagsChange) );
	mv_renderNearest.SetDescription("Used to eliminate z-buffer artifacts when rendering from first person view");
	AddVariable( mv_noDecals,"NoStaticDecals",functor(*this,&CEntityObject::OnRenderFlagsChange) );

	AddVariable(mv_createdThroughPool, "CreatedThroughPool", _T("Created Through Pool"));
}

//////////////////////////////////////////////////////////////////////////&
void CEntityObject::Done()
{
	if (m_pFlowGraph)
	{
		CFlowGraphManager* pFGMGR = GetIEditor()->GetFlowGraphManager();
		if (pFGMGR) 
			pFGMGR->UnregisterAndResetView(m_pFlowGraph, this);
	}
	SAFE_RELEASE(m_pFlowGraph);

	DeleteEntity();
	UnloadScript();

	if (m_trackGizmo)
	{
		RemoveGizmo( m_trackGizmo );
		m_trackGizmo = 0;
	}
	if (m_pAnimNode)
	{
		m_pAnimNode->SetNodeOwner(0);
		GetIEditor()->Notify(eNotify_OnUpdateTrackView);
	}
	ReleaseEventTargets();
	RemoveAllEntityLinks();

	CBaseObject::Done();
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::FreeGameData()
{
	DeleteEntity();
}

//////////////////////////////////////////////////////////////////////////
bool CEntityObject::Init( IEditor *ie,CBaseObject *prev,const CString &file )
{
	CBaseObject::Init( ie,prev,file );

	if (prev)
	{
		CEntityObject *pe = (CEntityObject*)prev;
	
		// Clone Properties.
		if (pe->m_properties)
		{
			m_properties = CloneProperties(pe->m_properties);
		}
		if (pe->m_properties2)
		{
			m_properties2 = CloneProperties(pe->m_properties2);
		}
		// When cloning entity, do not get properties from script.
		SetClass( pe->GetEntityClass(),false,false );
		SpawnEntity();

		if (pe->m_pFlowGraph)
		{
			SetFlowGraph( (CFlowGraph*)pe->m_pFlowGraph->Clone() );
		}

		mv_createdThroughPool = pe->mv_createdThroughPool;

		UpdatePropertyPanel();
	}
	else if (!file.IsEmpty())
	{
		SetUniqName( file );
		m_entityClass = file;

		IEntityPoolManager *pPoolManager = gEnv->pEntitySystem->GetIEntityPoolManager();
		if (pPoolManager && pPoolManager->IsClassDefaultBookmarked(m_entityClass.GetString()))
		{
			mv_createdThroughPool = true;
		}
	}

	return true;
}

//////////////////////////////////////////////////////////////////////////
CEntityObject* CEntityObject::FindFromEntityId( EntityId id )
{
	CEntityObject *pEntity = stl::find_in_map( s_entityIdMap,id,0 );
	return pEntity;
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::SetAnimNode( IAnimNode *pAnimNode )
{
	m_pAnimNode = pAnimNode;
	UpdateTrackGizmo();
}

//////////////////////////////////////////////////////////////////////////
IAnimNode* CEntityObject::HasOwnedAnimNode() const
{
	for(int k=0; k<GetIEditor()->GetMovieSystem()->GetNumSequences(); ++k)
	{
		IAnimSequence *seq = GetIEditor()->GetMovieSystem()->GetSequence(k);
		for (int i = 0,num = seq->GetNodeCount(); i < num; ++i)
		{
			IAnimNode *pAnimNode = seq->GetNode(i);

			// Find owner entity.
			if (IsOwningAnimNode(pAnimNode))
			{
				return pAnimNode;
			}
		}
	}

	return NULL;
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::GetAllRelatedAnimNodes(std::vector<IAnimNode*>& nodeList) const
{
	nodeList.clear();

	for(int k=0; k<GetIEditor()->GetMovieSystem()->GetNumSequences(); ++k)
	{
		IAnimSequence *seq = GetIEditor()->GetMovieSystem()->GetSequence(k);
		for (int i = 0,num = seq->GetNodeCount(); i < num; ++i)
		{
			IAnimNode *pAnimNode = seq->GetNode(i);

			// Find owner entity.
			if (IsOwningAnimNode(pAnimNode))
			{
				nodeList.push_back(pAnimNode);
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
bool CEntityObject::IsOwningAnimNode( IAnimNode *pAnimNode ) const
{
	assert(pAnimNode);

	if (!pAnimNode)
		return false;

	IAnimNodeOwner *pOwner = pAnimNode->GetNodeOwner();
	if (pOwner)
	{
		CEntityObject *pEntity = static_cast<CEntityObject*>(pOwner);
		return pEntity == this;
	}

	EntityGUID *pEntityGuid = pAnimNode->GetEntityGuid();
	if (pEntityGuid == 0 || *pEntityGuid == 0)
	{
		return false;
	}

	// Find owner entity.
	IEntity *pIEntity = pAnimNode->GetEntity();
	if (pIEntity)
	{
		// Find owner editor entity.
		CEntityObject *pEntity = CEntityObject::FindFromEntityId( pIEntity->GetId() );
		return pEntity == this;
	}

	// If all things fail, do this last check.
	int nodeType = pAnimNode->GetType();
	if (nodeType == ANODE_ENTITY || nodeType == ANODE_CAMERA)
	{
		uint32 animNodeId = pAnimNode->GetId();
		if (GetId().Data1 == animNodeId)
			return true;
	}

	return false;
}

//////////////////////////////////////////////////////////////////////////
bool CEntityObject::IsSameClass( CBaseObject *obj )
{
	if (GetClassDesc() == obj->GetClassDesc())
	{
		CEntityObject *ent = (CEntityObject*)obj;
		return GetScript() == ent->GetScript();
	}
	return false;
}

//////////////////////////////////////////////////////////////////////////
bool CEntityObject::ConvertFromObject( CBaseObject *object )
{
	CBaseObject::ConvertFromObject(object);

	if (object->IsKindOf(RUNTIME_CLASS(CEntityObject)))
	{
		CEntityObject *pObject = (CEntityObject*)object;

		mv_outdoor = pObject->mv_outdoor;
		mv_castShadow = pObject->mv_castShadow;
		mv_ratioLOD = pObject->mv_ratioLOD;
		mv_ratioViewDist = pObject->mv_ratioViewDist;
		mv_globalInSW = pObject->mv_globalInSW;
		mv_hiddenInGame = pObject->mv_hiddenInGame;
		mv_recvWind = pObject->mv_recvWind;
		mv_bending = pObject->mv_bending;
		mv_renderNearest = pObject->mv_renderNearest;
		mv_noDecals = pObject->mv_noDecals;

		mv_createdThroughPool = pObject->mv_createdThroughPool;
	}

	return false;
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::SetLookAt( CBaseObject *target )
{
	CBaseObject::SetLookAt(target);
}

//////////////////////////////////////////////////////////////////////////
IPhysicalEntity* CEntityObject::GetCollisionEntity() const
{
	// Returns physical object of entity.
	if (m_entity)
		return m_entity->GetPhysics();
	return 0;
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::GetLocalBounds( AABB &box )
{
	box = m_box;
}

//////////////////////////////////////////////////////////////////////////
bool CEntityObject::HitTestEntity( HitContext &hc,bool &bHavePhysics )
{
	bHavePhysics = true;
	IPhysicalWorld *pPhysWorld = GetIEditor()->GetSystem()->GetIPhysicalWorld();
	// Test 3D viewport.
	IPhysicalEntity *physic = 0;

	ICharacterInstance *pCharacter = m_entity->GetCharacter(0);
	if (pCharacter)
	{	
		physic = pCharacter->GetISkeletonPose()->GetCharacterPhysics();
		if (physic)
		{
			int type = physic->GetType();
			if (type == PE_NONE || type == PE_PARTICLE || type == PE_ROPE || type == PE_SOFT)
				physic = 0;
			else if (physic->GetStatus( &pe_status_nparts() ) == 0)
				physic = 0;
		}
		if (physic)
		{
			ray_hit hit;
			int col = pPhysWorld->RayTraceEntity( physic,hc.raySrc,hc.rayDir*10000.0f,&hit );
			if (col <= 0)
				return false;
			hc.dist = hit.dist;
			return true;
		}
	}

	physic = m_entity->GetPhysics();
	if (physic)
	{
		int type = physic->GetType();
		if (type == PE_NONE || type == PE_PARTICLE || type == PE_ROPE || type == PE_SOFT)
			physic = 0;
		else if (physic->GetStatus( &pe_status_nparts() ) == 0)
			physic = 0;
	}
	// Now if box intersected try real geometry ray test.
	if (physic)
	{
		ray_hit hit;
		int col = pPhysWorld->RayTraceEntity( physic,hc.raySrc,hc.rayDir*10000.0f,&hit );
		if (col <= 0)
			return false;
		hc.dist = hit.dist;
		return true;
	}
	else
	{
		bHavePhysics = false;
	}
	return false;
}

//////////////////////////////////////////////////////////////////////////
bool CEntityObject::HitTest( HitContext &hc )
{
	if (!hc.b2DViewport)
	{
		// Test 3D viewport.
		if (m_entity)
		{
			bool bHavePhysics = false;
			if (HitTestEntity( hc,bHavePhysics ))
				return true;
			if (bHavePhysics)
			{
				return false;
			}
		}
		if (m_visualObject && !gSettings.viewports.bShowIcons && !gSettings.viewports.bShowSizeBasedIcons)
		{
			Matrix34 tm = GetWorldTM();
			float sz = m_helperScale * gSettings.gizmo.helpersScale;
			tm.ScaleColumn( Vec3(sz,sz,sz) );
			primitives::ray aray; aray.origin = hc.raySrc; aray.dir = hc.rayDir*10000.0f;

			IGeomManager *pGeomMgr = GetIEditor()->GetSystem()->GetIPhysicalWorld()->GetGeomManager();
			IGeometry *pRay = pGeomMgr->CreatePrimitive(primitives::ray::type, &aray);
			geom_world_data gwd;
			gwd.offset = tm.GetTranslation();
			gwd.scale = tm.GetColumn0().GetLength();
			gwd.R = Matrix33(tm);
			geom_contact *pcontacts = 0;
			WriteLockCond lock;
			int col = (m_visualObject->GetPhysGeom() && m_visualObject->GetPhysGeom()->pGeom) ? m_visualObject->GetPhysGeom()->pGeom->IntersectLocked(pRay, &gwd,0, 0, pcontacts,lock) : 0;
			pGeomMgr->DestroyGeometry(pRay);
			if (col > 0)
			{
				if (pcontacts)
					hc.dist = pcontacts[col-1].t;
				return true;
			}
		}
	}
	/*
	else if (m_entity)
	{
	float dist = FLT_MAX;
	bool bHaveHit = false;
	CEntityObject entobj;
	IPhysicalWorld *physWorld = GetIEditor()->GetSystem()->GetIPhysicalWorld();
	int numobj = m_entity->GetNumObjects();
	if (numobj == 0)
	{
	hc.weakHit = true;
	return true;
	}
	vector origin = hc.raySrc;
	vector dir = hc.rayDir*10000.0f;
	Matrix tm;
	GetMatrix( tm );
	tm.Transpose();
	for (int i = 0; i < numobj; i++)
	{
	m_entity->GetEntityObject(i,entobj);
	if (entobj.object && entobj.object->GetPhysGeom())
	{
	ray_hit hit;
	int col = physWorld->RayTraceGeometry( entobj.object->GetPhysGeom(),(float*)tm.m_values,origin,dir,&hit );
	if (col > 0)
	{
	dist = __min(dist,hit.dist);
	bHaveHit = true;
	}
	}
	}
	if (bHaveHit)
	{
	hc.dist = dist;
	return true;
	}
	else
	{
	return false;
	}
	}
	*/


	//////////////////////////////////////////////////////////////////////////
	if ((m_bDisplayBBox && gSettings.viewports.bShowTriggerBounds) || hc.b2DViewport)
	{
		float hitEpsilon = hc.view->GetScreenScaleFactor( GetWorldPos() ) * 0.01f;
		float hitDist;

		float fScale = GetScale().x;
		AABB boxScaled;
		boxScaled.min = m_box.min*fScale;
		boxScaled.max = m_box.max*fScale;

		Matrix34 invertWTM = GetWorldTM();
		invertWTM.Invert();
		//Vec3 xformedRaySrc = invertWTM*hc.raySrc;
		//Vec3 xformedRayDir = GetNormalized( invertWTM*hc.rayDir );


		Vec3 xformedRaySrc = invertWTM.TransformPoint(hc.raySrc);
		Vec3 xformedRayDir = invertWTM.TransformVector(hc.rayDir);
		xformedRayDir.Normalize();

		{
			Vec3 intPnt;
			// Check intersection with bbox edges.
			if (Intersect::Ray_AABBEdge( xformedRaySrc,xformedRayDir,boxScaled,hitEpsilon,hitDist,intPnt ))
			{
				hc.dist = xformedRaySrc.GetDistance(intPnt);
				return true;
			}
		}
	}

	return false;
}

//////////////////////////////////////////////////////////////////////////
bool CEntityObject::HitHelperTest( HitContext &hc )
{
	bool bResult = CBaseObject::HitHelperTest(hc);

	if (!bResult && m_entity && GetIEditor()->GetGameEngine()->GetSimulationMode())
	{
		bResult = HitHelperAtTest(hc, m_entity->GetWorldPos());
	}

	return bResult;
}

//////////////////////////////////////////////////////////////////////////
bool CEntityObject::HitTestRect( HitContext &hc )
{
	bool bResult = false;
	
	if (m_visualObject && !gSettings.viewports.bShowIcons && !gSettings.viewports.bShowSizeBasedIcons)
	{
		AABB box;
		box.SetTransformedAABB( GetWorldTM(),m_visualObject->GetAABB() );
		bResult = HitTestRectBounds( hc,box );
	}
	else
	{
		bResult = CBaseObject::HitTestRect( hc );
		if (!bResult && m_entity && GetIEditor()->GetGameEngine()->GetSimulationMode())
		{
			AABB box;
			if (hc.bUseSelectionHelpers)
			{
				box.max = box.min = m_entity->GetWorldPos();
			}
			else
			{
				// Retrieve world space bound box.
				m_entity->GetWorldBounds( box );
			}

			bResult = HitTestRectBounds( hc,box );
		}
	}

	return bResult;
}

//////////////////////////////////////////////////////////////////////////
int CEntityObject::MouseCreateCallback( CViewport *view,EMouseEvent event,CPoint &point,int flags )
{
	if (event == eMouseMove || event == eMouseLDown)
	{
		Vec3 pos;
		// Rise Entity above ground on Bounding box amount.
		if (GetIEditor()->GetAxisConstrains() != AXIS_TERRAIN)
		{
			pos = view->MapViewToCP(point);
		}
		else
		{
			// Snap to terrain.
			bool hitTerrain;
			pos = view->ViewToWorld( point,&hitTerrain );
			if (hitTerrain)
			{
				pos.z = GetIEditor()->GetTerrainElevation(pos.x,pos.y);
				pos.z = pos.z - m_box.min.z;
			}
			pos = view->SnapToGrid(pos);
		}
		SetPos( pos );

		// When we create an entity, the first placement happens with the
		// undo suspended. For this reason, we must put some special case here.
		GetIEditor()->GetConsoleSync()->OnObjectModified(this,false,true);
		
		if (event == eMouseLDown) {
			SW_TEST_OBJ_PLACETO_MCB(GetPos(), GetLayer(), true);
			SW_ON_OBJ_NEW(this);
			return MOUSECREATE_OK;
		}
		return MOUSECREATE_CONTINUE;
	}
	return CBaseObject::MouseCreateCallback( view,event,point,flags );
}

//////////////////////////////////////////////////////////////////////////
bool CEntityObject::CreateGameObject()
{
	if (!m_pClass)
	{
		if (!m_entityClass.IsEmpty())
			SetClass( m_entityClass,true );
	}
	if (!m_entity)
	{
		if (!m_entityClass.IsEmpty())
			SpawnEntity();
	}
	return true;
}

//////////////////////////////////////////////////////////////////////////
bool CEntityObject::SetClass( const CString &entityClass,bool bForceReload,bool bGetScriptProperties,XmlNodeRef xmlProperties,XmlNodeRef xmlProperties2 )
{
	// [9/29/2009 evgeny] Added "&& !bGetScriptProperties", because if bGetScriptProperties,
	// then we should reach the other CEntity::SetClass method (at the end of this method)
	if (entityClass == m_entityClass && m_pClass != 0 && !bForceReload && !bGetScriptProperties)
		return true;
	if (entityClass.IsEmpty())
		return false;

	m_entityClass = entityClass;
	m_bLoadFailed = false;

	UnloadScript();

	if (!IsCreateGameObjects())
		return false;

	CEntityScript* pClass = CEntityScriptRegistry::Instance()->Find( m_entityClass );
	if (!pClass)
	{
		OnLoadFailed();
		return false;
	}
	SetClass( pClass,bForceReload,bGetScriptProperties,xmlProperties,xmlProperties2 );
	
	return true;
}

//////////////////////////////////////////////////////////////////////////
bool CEntityObject::SetClass( CEntityScript* pClass,bool bForceReload,bool bGetScriptProperties,XmlNodeRef xmlProperties,XmlNodeRef xmlProperties2 )
{
	assert(pClass);

	if (pClass == m_pClass && !bForceReload)
		return true;
	m_pClass = pClass;

	m_bLoadFailed = false;
	// Load script if its not loaded yet.
	if (!m_pClass->IsValid())
	{
		if (!m_pClass->Load())
		{
			OnLoadFailed();
			return false;
		}
	}

	int nTexIcon = pClass->GetTextureIcon();
	if (nTexIcon)
	{
		SetTextureIcon( pClass->GetTextureIcon() );
	}

	//////////////////////////////////////////////////////////////////////////
	// Turns of Cast Shadow variable for Light entities.
	if (stricmp(pClass->GetName(),CLASS_LIGHT) == 0)
	{
    mv_castShadow = false;
		mv_castShadow.SetFlags( mv_castShadow.GetFlags()|IVariable::UI_INVISIBLE );
		mv_ratioLOD.SetFlags( mv_ratioLOD.GetFlags()|IVariable::UI_INVISIBLE );
		mv_recvWind.SetFlags( mv_recvWind.GetFlags()|IVariable::UI_INVISIBLE );
		mv_noDecals.SetFlags( mv_noDecals.GetFlags()|IVariable::UI_INVISIBLE );

		Vec3 vNoScale(1.0f,1.0f,1.0f);

		if ( GetScale().GetSquaredDistance(vNoScale) !=0 )
		{
			bool bWasForceScale=GetForceScale();
			SetForceScale(true);
			SetScale(vNoScale);

			if (!bWasForceScale)
				m_bForceScale=false;

			CErrorRecord error;
			error.pObject=this;
			error.count=1;
			error.severity = CErrorRecord::ESEVERITY_WARNING;
			error.error.Format( "Light %s had scale different than x=1,y=1,z=1. This issue was fixed to avoid light scissors effect",this->GetName() );
			error.description="Scale was set to 1";
			GetIEditor()->GetErrorReport()->ReportError(error);
		}
	}

	//////////////////////////////////////////////////////////////////////////
	// Make visual editor object for this entity.
	//////////////////////////////////////////////////////////////////////////
	if (!m_pClass->GetVisualObject().IsEmpty())
	{
		//m_entity->LoadObject( 0,m_pClass->GetVisualObject(),1 );
		//m_entity->DrawObject( 0,ETY_DRAW_NORMAL );
		m_visualObject = GetIEditor()->Get3DEngine()->LoadStatObj( m_pClass->GetVisualObject(), NULL, NULL, false );
		if (m_visualObject)
			m_visualObject->AddRef();
	}

	bool bUpdateUI = false;
	// Create Entity properties from Script properties..
	if (bGetScriptProperties && m_prototype == NULL && m_pClass->GetProperties() != NULL)
	{
		bUpdateUI = true;
		CVarBlockPtr oldProperties = m_properties;
		m_properties = CloneProperties( m_pClass->GetProperties() );

		if (xmlProperties)
		{
			s_ignorePropertiesUpdate = true;
			m_properties->Serialize( xmlProperties,true );
			s_ignorePropertiesUpdate = false;
		}
		else if (oldProperties)
		{
			// If we had propertied before copy their values to new script.
			s_ignorePropertiesUpdate = true;
			m_properties->CopyValuesByName(oldProperties);
			s_ignorePropertiesUpdate = false;
		}

		// High limit for snow flake count for snow entity.
		if (IVariable* pSubBlockSnowFall = m_properties->FindVariable( "SnowFall" ))
		{
			if (IVariable* pSnowFlakeCount = FindVariableInSubBlock( m_properties, pSubBlockSnowFall, "nSnowFlakeCount"))
			{
				pSnowFlakeCount->SetLimits(0, 1000000, 0, true, true);
			}
		}

		// For all lights, we set the minimum radius to 0.01, as a radius of
		// 0 would bring serious numerical problems.
		// For old light entities increment version number
		if (stricmp(pClass->GetName(),CLASS_LIGHT) == 0)
			AdjustLightProperties( m_properties, NULL );
	}

	// Create Entity properties from Script properties..
	if (bGetScriptProperties && m_pClass->GetProperties2() != NULL)
	{
		bUpdateUI = true;
		CVarBlockPtr oldProperties = m_properties2;
		m_properties2 = CloneProperties( m_pClass->GetProperties2() );

		if (xmlProperties2)
		{
			s_ignorePropertiesUpdate = true;
			m_properties2->Serialize( xmlProperties2,true );
			s_ignorePropertiesUpdate = false;
		}
		else if (oldProperties)
		{
			// If we had propertied before copy thier values to new script.
			s_ignorePropertiesUpdate = true;
			m_properties2->CopyValuesByName(oldProperties);
			s_ignorePropertiesUpdate = false;
		}
		
		if ((stricmp(pClass->GetName(),CLASS_RIGIDBODY_LIGHT) == 0 || stricmp(pClass->GetName(),CLASS_DESTROYABLE_LIGHT)==0) && m_properties2 )
		{
			AdjustLightProperties( m_properties2, "LightProperties_Base" );
			AdjustLightProperties( m_properties2, "LightProperties_Destroyed" );
		}
	}

	{
		// Populate empty events from the script.
		for (int i = 0,num = m_pClass->GetEmptyLinkCount(); i < num; i++)
		{
			const CString &linkName = m_pClass->GetEmptyLink(i);
			int j = 0;
			int numlinks = (int)m_links.size();
			for (j = 0; j < numlinks; j++)
			{
				if (strcmp(m_links[j].name,linkName) == 0)
					break;
			}
			if (j >= numlinks)
				AddEntityLink(linkName,GUID_NULL);
		}
	}

	return true;
}


IVariable* CEntityObject::FindVariableInSubBlock( CVarBlockPtr& properties, IVariable* pSubBlockVar, const char* pVarName )
{
	IVariable* pVar = pSubBlockVar ? properties->FindChildVar( pVarName, pSubBlockVar ) : properties->FindVariable( pVarName );
	return pVar;
}


//////////////////////////////////////////////////////////////////////////

void CEntityObject::AdjustLightProperties( CVarBlockPtr& properties, const char* pSubBlock )
{
	IVariable* pSubBlockVar = pSubBlock ? properties->FindVariable( pSubBlock ) : NULL;

	if(IVariable* pRadius = FindVariableInSubBlock( properties, pSubBlockVar, "Radius" ))
	{
		// The value of 0.01 was found through asking Crysis 2 designer 
		// team.
		pRadius->SetLimits(0.01f,100.0f,0.0f,true,false);
	}

	if (IVariable* pProjectorFov = FindVariableInSubBlock( properties, pSubBlockVar, "fProjectorFov" ))
	{
		pProjectorFov->SetLimits(0.01f,160.0f,0.0f,true,true);
	}

	bool bCastShadowLegacy = false;  // Backward compatibility for existing shadow casting lights
	if(IVariable* pCastShadowVarLegacy = FindVariableInSubBlock( properties, pSubBlockVar, "bCastShadow"))
	{
		pCastShadowVarLegacy->SetFlags( pCastShadowVarLegacy->GetFlags()|IVariable::UI_INVISIBLE );

		if(pCastShadowVarLegacy->GetDisplayValue()[0] != '0')
		{
			bCastShadowLegacy = true;
			pCastShadowVarLegacy->SetDisplayValue("0");
		}
	}

	if(IVariable* pCastShadowVar = FindVariableInSubBlock( properties, pSubBlockVar, "nCastShadows"))
	{
		if(bCastShadowLegacy)
		{
			pCastShadowVar->SetDisplayValue("1");
		}
		pCastShadowVar->SetDataType(IVariable::DT_UIENUM);
		pCastShadowVar->SetFlags( pCastShadowVar->GetFlags()|IVariable::UI_UNSORTED );
	}

	if(IVariable* pVer = FindVariableInSubBlock( properties, pSubBlockVar, "_nVersion"))
	{
		int version = -1;
		pVer->Get(version);
		if(version == -1)
		{
			IVariable* pCubemap =  FindVariableInSubBlock( properties, pSubBlockVar, "texture_deferred_cubemap");
			assert(pCubemap);
			IVariable* pAmbient =  FindVariableInSubBlock( properties, pSubBlockVar, "bAmbientLight");
			assert(pAmbient);
			if(pCubemap && pAmbient)
			{
				CString sCubemap;
				pCubemap->Get(sCubemap);
				if(!sCubemap.IsEmpty())
					pAmbient->Set(true);
				else
					pAmbient->Set(false);
			}
			else
				assert(0);
			version++;
			pVer->Set(version);
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::SpawnEntity()
{
	if (!m_pClass)
		return;

	// Do not spawn second time.
	if (m_entity)
		return;

	m_bLoadFailed = false;

	IEntitySystem *pEntitySystem = GetIEditor()->GetSystem()->GetIEntitySystem();

	if (m_entityId != 0)
	{
		if (pEntitySystem->IsIDUsed( m_entityId ))
			m_entityId = 0;
	}

	SEntitySpawnParams params;
	params.pClass = m_pClass->GetClass();
	params.nFlags = 0;
	params.sName = (const char*)GetName();
	params.vPosition = GetPos();
	params.qRotation = GetRotation();
	params.id = m_entityId;

	m_bLight = strstr( params.pClass->GetScriptFile(), "/Lights/" ) ? true : false;

	if (m_prototype)
		params.pArchetype = m_prototype->GetIEntityArchetype();

	if (mv_castShadow)
		params.nFlags |= ENTITY_FLAG_CASTSHADOW;
	//if (mv_recvShadow)
		//params.nFlags |= ENTITY_FLAG_RECVSHADOW;
	if (mv_outdoor)
		params.nFlags |= ENTITY_FLAG_OUTDOORONLY;
  if (mv_recvWind)
    params.nFlags |= ENTITY_FLAG_RECVWIND;
  if (mv_noDecals)
    params.nFlags |= ENTITY_FLAG_NO_DECALNODE_DECALS;
  
  //if (mv_motionBlurAmount != 1.0f)
  ////  params.nFlags |= ENTITY_FLAG_USEMOTIONBLUR;
	
	if(params.id==0)
		params.bStaticEntityId=true; // Tells to Entity system to generate new static id.

	params.guid = ToEntityGuid(GetId());
	
	// Spawn Entity but not initialize it.
	m_entity = pEntitySystem->SpawnEntity( params,false );
	if (m_entity)
	{
		m_entityId = m_entity->GetId();

		s_entityIdMap[m_entityId] = this;

		if (m_properties != NULL && m_prototype == NULL)
		{
			m_pClass->SetProperties( m_entity,m_properties,false );
		}
		if (m_properties2 != NULL)
		{
			m_pClass->SetProperties2( m_entity,m_properties2,false );
		}

		// Bind to parent.
		BindToParent();
		BindIEntityChilds();

		//m_pClass->SetCurrentProperties( this );
		m_pClass->SetEventsTable( this );

		UpdateIEntityLinks(false);

		// Mark this entity non destroyable.
		m_entity->AddFlags(ENTITY_FLAG_UNREMOVABLE);
		
		// Force transformation on entity.
		XFormGameEntity();

		// Specialized sound code to set a max-distance-sound-sphere until CSoundObject gets revived
		if(m_pClass && !strcmp(m_pClass->GetName(), "SoundEventSpot"))
			SetSoundEventSpotSphere();

		//////////////////////////////////////////////////////////////////////////
		// Now initialize entity.
		//////////////////////////////////////////////////////////////////////////
		if (!pEntitySystem->InitEntity( m_entity,params ))
		{
			m_entity = 0;
			OnLoadFailed();
			return;
		}

		m_entity->Hide( !m_bVisible );

		//////////////////////////////////////////////////////////////////////////
		// If have material, assign it to the entity.
		if (GetMaterial())
		{
			UpdateMaterialInfo();
		}

		// Update render flags of entity (Must be after InitEntity).
		OnRenderFlagsChange(0);

		if (!m_physicsState)
		{
			m_entity->SetPhysicsState( m_physicsState );
		}

		//////////////////////////////////////////////////////////////////////////
		// Check if needs to display bbox for this entity.
		//////////////////////////////////////////////////////////////////////////
		m_bCalcPhysics = true;
		if (m_entity->GetPhysics() != 0)
		{
			m_bDisplayBBox = false;
			if (m_entity->GetPhysics()->GetType() == PE_SOFT)
			{
				m_bCalcPhysics = false;
				//! Ignore entity being updated from physics.
				m_entity->SetFlags(m_entity->GetFlags() | ENTITY_FLAG_IGNORE_PHYSICS_UPDATE);
			}
		}
		else
		{
			if (m_pClass->GetFlags() & ENTITY_SCRIPT_SHOWBOUNDS)
			{
				m_bDisplayBBox = true;
				m_bDisplaySolidBBox = true;
			}
			else
			{
				m_bDisplayBBox = false;
				m_bDisplaySolidBBox = false;
			}

			m_bDisplayAbsoluteRadius = (m_pClass->GetFlags() & ENTITY_SCRIPT_ABSOLUTERADIUS) ? true : false;
		}

		m_bIconOnTop = (m_pClass->GetFlags() & ENTITY_SCRIPT_ICONONTOP) ? true : false;
		if (m_bIconOnTop)
			SetFlags(OBJFLAG_SHOW_ICONONTOP);
		else
			ClearFlags(OBJFLAG_SHOW_ICONONTOP);

		m_bDisplayArrow = (m_pClass->GetFlags() & ENTITY_SCRIPT_DISPLAY_ARROW) ? true : false;

		//////////////////////////////////////////////////////////////////////////
		// Calculate entity bounding box.
		CalcBBox();
		
		//////////////////////////////////////////////////////////////////////////
		// Assign entity pointer to animation node.
		if (m_pAnimNode)
		{
			m_pAnimNode->SetEntityGuid( ToEntityGuid(GetId()) );
			GetIEditor()->Notify(eNotify_OnUpdateTrackView);
		}

		if (m_pFlowGraph)
		{
			// Re-apply entity for flow graph.
			m_pFlowGraph->SetEntity(this, true);

			IEntityFlowGraphProxy *pFlowGraphProxy = (IEntityFlowGraphProxy*)m_entity->CreateProxy(ENTITY_PROXY_FLOWGRAPH);
			IFlowGraph* pGameFlowGraph = m_pFlowGraph->GetIFlowGraph();
			pFlowGraphProxy->SetFlowGraph( pGameFlowGraph );
			if (pGameFlowGraph)
				pGameFlowGraph->SetActive(true);

		}
		if(m_physicsState)
			m_entity->SetPhysicsState( m_physicsState );
	}
	else
	{
		OnLoadFailed();
	}

	//?
	UpdatePropertyPanel();
	CheckSpecConfig( );
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::DeleteEntity()
{
	if (m_entity)
	{
		UnbindIEntity();
		m_entity->ClearFlags(ENTITY_FLAG_UNREMOVABLE);
		IEntitySystem *pEntitySystem = gEnv->pEntitySystem;
		pEntitySystem->RemoveEntity( m_entity->GetId(),true );
		s_entityIdMap.erase( m_entityId );
	}
	m_entity = 0;
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::UnloadScript()
{
	if (m_visualObject)
		m_visualObject->Release();
	m_visualObject = 0;
	m_pClass = 0;
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::XFormGameEntity()
{
	// Make sure components are correctly calculated.
	const Matrix34 &tm = GetWorldTM();
	if (m_entity)
	{
		int nWhyEntityFlag = ENTITY_XFORM_EDITOR;
		if (GetParent() && !m_entity->GetParent())
		{
			m_entity->SetWorldTM( tm,nWhyEntityFlag );
		}
		else if (GetLookAt())
		{
			m_entity->SetWorldTM( tm,nWhyEntityFlag );
		}
		else
		{
			m_entity->SetPosRotScale( GetPos(),GetRotation(),GetScale(),nWhyEntityFlag );
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::CalcBBox()
{
	if (m_entity)
	{
		// Get Local bounding box of entity.
		m_entity->GetLocalBounds( m_box );

		if(m_box.min.x >= m_box.max.x || m_box.min.y >= m_box.max.y || m_box.min.z >= m_box.max.z)
		{
			if (m_visualObject)
			{
				Vec3 minp = m_visualObject->GetBoxMin()*m_helperScale*gSettings.gizmo.helpersScale;
				Vec3 maxp = m_visualObject->GetBoxMax()*m_helperScale*gSettings.gizmo.helpersScale;
				m_box.Add( minp );
				m_box.Add( maxp );
			}
			else
				m_box=AABB(ZERO);
		}
		float minSize = 0.0001f;
		if (fabs(m_box.max.x-m_box.min.x)+fabs(m_box.max.y-m_box.min.y)+fabs(m_box.max.z-m_box.min.z) < minSize)
		{
			m_box.min = -Vec3( minSize,minSize,minSize );
			m_box.max = Vec3( minSize,minSize,minSize );
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::SetName( const CString &name )
{
	if (name == GetName())
		return;

	CBaseObject::SetName( name );
	if (m_entity)
		m_entity->SetName( (const char*)GetName() );
	if (m_pAnimNode)
	{
		m_pAnimNode->SetName( name );
	}
	GetIEditor()->Notify(eNotify_OnUpdateTrackView);
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::BeginEditParams( IEditor *ie,int flags )
{
	CBaseObject::BeginEditParams( ie,flags );

	if (m_properties2 != NULL)
	{
		if (!s_propertiesPanel2)
			s_propertiesPanel2 = new CPropertiesPanel( AfxGetMainWnd() );
		else
			s_propertiesPanel2->DeleteVars();
		s_propertiesPanel2->AddVars( m_properties2 );
		if (!s_propertiesPanelIndex2)
			s_propertiesPanelIndex2 = AddUIPage( CString(GetTypeName()) + " Properties2",s_propertiesPanel2 );

		if(s_propertiesPanel2->GetPropertyCtrl())
			s_propertiesPanel2->GetPropertyCtrl()->SetUpdateObjectCallback( functor(*this,&CBaseObject::OnPropertyChanged) );
	}

	if (!m_prototype)
	{
		if (m_properties != NULL)
		{
			if (!s_propertiesPanel)
				s_propertiesPanel = new CPropertiesPanel( AfxGetMainWnd() );
			else
				s_propertiesPanel->DeleteVars();
			s_propertiesPanel->AddVars( m_properties );
			if (!s_propertiesPanelIndex)
				s_propertiesPanelIndex = AddUIPage( CString(GetTypeName()) + " Properties",s_propertiesPanel );

			if(s_propertiesPanel->GetPropertyCtrl())
				s_propertiesPanel->GetPropertyCtrl()->SetUpdateObjectCallback( functor(*this,&CBaseObject::OnPropertyChanged) );
		}
	}

	if (!m_panel && m_entity)
	{
		m_panel = new CEntityPanel(AfxGetMainWnd());
		m_panel->Create( CEntityPanel::IDD,AfxGetMainWnd() );
		m_rollupId = AddUIPage( CString("Entity: ")+m_entityClass,m_panel );
	}
	if (m_panel && m_panel->m_hWnd)
		m_panel->SetEntity(this);

	//////////////////////////////////////////////////////////////////////////
	// Links Panel
	if (!s_entityLinksPanel && m_entity)
	{
		s_entityLinksPanel = new CEntityLinksPanel(AfxGetMainWnd());
		s_entityLinksPanel->Create( CEntityLinksPanel::IDD,AfxGetMainWnd() );
		s_entityLinksPanelIndex = AddUIPage( "Entity Links",s_entityLinksPanel,true,-1,false );
	}
	if (s_entityLinksPanel)
		s_entityLinksPanel->SetEntity( this );

	//////////////////////////////////////////////////////////////////////////
	// Events panel
	if (!s_entityEventsPanel && m_entity)
	{
		s_entityEventsPanel = new CEntityEventsPanel(AfxGetMainWnd());
		s_entityEventsPanel->Create( CEntityEventsPanel::IDD,AfxGetMainWnd() );
		s_entityEventsPanelIndex = AddUIPage( "Entity Events",s_entityEventsPanel,true,-1,false );
		GetIEditor()->ExpandRollUpPage( s_entityEventsPanelIndex,FALSE );
	}
	if (s_entityEventsPanel)
		s_entityEventsPanel->SetEntity( this );
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::EndEditParams( IEditor *ie )
{
	if(s_propertiesPanel2 && s_propertiesPanel2->GetPropertyCtrl())
		s_propertiesPanel2->GetPropertyCtrl()->ClearUpdateObjectCallback();

	if(s_propertiesPanel && s_propertiesPanel->GetPropertyCtrl())
		s_propertiesPanel->GetPropertyCtrl()->ClearUpdateObjectCallback();

	if (s_entityEventsPanelIndex != 0)
	{
		RemoveUIPage( s_entityEventsPanelIndex );
	}
	s_entityEventsPanelIndex = 0;
	s_entityEventsPanel = 0;

	if (s_entityLinksPanelIndex != 0)
	{
		RemoveUIPage( s_entityLinksPanelIndex );
	}
	s_entityLinksPanelIndex = 0;
	s_entityLinksPanel = 0;

	if (m_rollupId != 0)
		RemoveUIPage( m_rollupId );
	m_rollupId = 0;
	m_panel = 0;

	if (s_propertiesPanelIndex != 0)
		RemoveUIPage( s_propertiesPanelIndex );
	s_propertiesPanelIndex = 0;
	s_propertiesPanel = 0;

	if (s_propertiesPanelIndex2 != 0)
		RemoveUIPage( s_propertiesPanelIndex2 );
	s_propertiesPanelIndex2 = 0;
	s_propertiesPanel2 = 0;

	CBaseObject::EndEditParams( GetIEditor() );
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::UpdatePropertyPanel()
{
	// If user interface opened reload properties.
	if (s_propertiesPanel && m_properties != 0)
	{
		s_propertiesPanel->DeleteVars();
		s_propertiesPanel->AddVars( m_properties );
	}

	// If user interface opened reload properties.
	if (s_propertiesPanel2 && m_properties2 != 0)
	{
		s_propertiesPanel2->DeleteVars();
		s_propertiesPanel2->AddVars( m_properties2 );
	}
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::BeginEditMultiSelParams( bool bAllOfSameType )
{
	CBaseObject::BeginEditMultiSelParams(bAllOfSameType);

	if (!bAllOfSameType)
		return;

	if (m_properties2 != NULL)
	{
		if (!s_propertiesPanel2)
			s_propertiesPanel2 = new CPropertiesPanel( AfxGetMainWnd() );
		else
			s_propertiesPanel2->DeleteVars();

		// Add all selected objects.
		CSelectionGroup *grp = GetIEditor()->GetSelection();
		for (int i = 0; i < grp->GetCount(); i++)
		{
			CEntityObject *ent = (CEntityObject*)grp->GetObject(i);
			if (ent->m_properties2)
				s_propertiesPanel2->AddVars( ent->m_properties2 );
		}
		if (!s_propertiesPanelIndex2)
			s_propertiesPanelIndex2 = AddUIPage( CString(GetTypeName()) + " Properties",s_propertiesPanel2 );

		if(s_propertiesPanel2->GetPropertyCtrl())
			s_propertiesPanel2->GetPropertyCtrl()->SetUpdateObjectCallback( functor(*this,&CBaseObject::OnMultiSelPropertyChanged) );
	}

	if (m_properties != NULL && m_prototype == NULL)
	{
		if (!s_propertiesPanel)
			s_propertiesPanel = new CPropertiesPanel( AfxGetMainWnd() );
		else
			s_propertiesPanel->DeleteVars();

		// Add all selected objects.
		CSelectionGroup *grp = GetIEditor()->GetSelection();
		for (int i = 0; i < grp->GetCount(); i++)
		{
			CEntityObject *ent = (CEntityObject*)grp->GetObject(i);
			CVarBlock *vb = ent->m_properties;
			if (vb)
				s_propertiesPanel->AddVars( vb );
		}
		if (!s_propertiesPanelIndex)
			s_propertiesPanelIndex = AddUIPage( CString(GetTypeName()) + " Properties",s_propertiesPanel );

		if(s_propertiesPanel->GetPropertyCtrl())
			s_propertiesPanel->GetPropertyCtrl()->SetUpdateObjectCallback( functor(*this,&CBaseObject::OnMultiSelPropertyChanged) );
	}
}
	
//////////////////////////////////////////////////////////////////////////
void CEntityObject::EndEditMultiSelParams()
{
	if(s_propertiesPanel && s_propertiesPanel->GetPropertyCtrl())
		s_propertiesPanel->GetPropertyCtrl()->ClearUpdateObjectCallback();

	if(s_propertiesPanel2 && s_propertiesPanel2->GetPropertyCtrl())
		s_propertiesPanel2->GetPropertyCtrl()->ClearUpdateObjectCallback();

	if (s_propertiesPanelIndex != 0)
		RemoveUIPage( s_propertiesPanelIndex );
	s_propertiesPanelIndex = 0;
	s_propertiesPanel = 0;

	if (s_propertiesPanelIndex2 != 0)
		RemoveUIPage( s_propertiesPanelIndex2 );
	s_propertiesPanelIndex2 = 0;
	s_propertiesPanel2 = 0;

	CBaseObject::EndEditMultiSelParams();
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::EnableEditParams(bool bEnable)
{
	CBaseObject::EnableEditParams(bEnable);
	if (s_propertiesPanel2)
		s_propertiesPanel2->SetEnable(bEnable);
	if (s_propertiesPanel)
		s_propertiesPanel->SetEnable(bEnable);
	if (m_panel)
		m_panel->SetEnable(bEnable);
	if (s_entityLinksPanel)
		s_entityLinksPanel->SetEnable(bEnable);
	if (s_entityEventsPanel)
		s_entityEventsPanel->SetEnable(bEnable);
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::EnableEditMultiSelParams(bool bEnable, bool bAllOfSameType)
{
	CBaseObject::EnableEditMultiSelParams(bEnable, bAllOfSameType);
	if (s_propertiesPanel2)
		s_propertiesPanel2->SetEnableMultiSel(bEnable, bAllOfSameType);
	if (s_propertiesPanel)
		s_propertiesPanel->SetEnableMultiSel(bEnable, bAllOfSameType);
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::SetSelected( bool bSelect )
{
	CBaseObject::SetSelected( bSelect );

	if (m_entity)
	{
		IEntityRenderProxy *pRenderProxy = (IEntityRenderProxy*)m_entity->GetProxy(ENTITY_PROXY_RENDER);
		if (pRenderProxy)
		{
			IRenderNode *pRenderNode = pRenderProxy->GetRenderNode();
			if (pRenderNode)
			{
				int flags = pRenderNode->GetRndFlags();
				if (bSelect && gSettings.viewports.bHighlightSelectedGeometry)
					flags |= ERF_SELECTED;
				else
					flags &= ~ERF_SELECTED;
				pRenderNode->SetRndFlags( flags );
			}
		}
	}
	if (m_pAnimNode)
		m_pAnimNode->SetFlags( (bSelect) ? (m_pAnimNode->GetFlags()|ANODE_FLAG_SELECTED) : (m_pAnimNode->GetFlags()&(~ANODE_FLAG_SELECTED)) );
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::OnPropertyChange( IVariable *var )
{
	if (s_ignorePropertiesUpdate)
		return;

	if (m_pClass != 0 && m_entity != 0)
	{
		if (m_properties != NULL && m_prototype == NULL)
			m_pClass->SetProperties( m_entity,m_properties,false );
		if (m_properties2)
			m_pClass->SetProperties2( m_entity,m_properties2,false );
		
		// Specialized sound code to set a max-distance-sound-sphere until CSoundObject gets revived
		if(m_pClass && !strcmp(m_pClass->GetName(), "SoundEventSpot"))
			SetSoundEventSpotSphere();

		m_pClass->CallOnPropertyChange(m_entity);
		
		// After change of properties bounding box of entity may change.
		CalcBBox();
		InvalidateTM(0);

		if (!GetIEditor()->IsUndoSuspended())
		{
			// When not loading objects ... for now...
			GetIEditor()->GetConsoleSync()->OnObjectModified(this,false,false);
			SW_ON_OBJ_MOD(this);
		}
	}
}

bool CEntityObject::GetEntityPropertyBool( const char *name )
{
	CVarBlock* pProperties = GetProperties();
	if (!pProperties)
		return false;

	IVariable *pVariable = pProperties->FindVariable(name);
	if (!pVariable)
		return false;

	if (pVariable->GetType() != IVariable::BOOL)
		return false;

	bool value;
	pVariable->Get(value);
	return value;
}

int CEntityObject::GetEntityPropertyInteger( const char *name )
{
	CVarBlock* pProperties = GetProperties();
	if (!pProperties)
		return 0;

	IVariable *pVariable = pProperties->FindVariable(name);
	if (!pVariable)
		return 0;

	if (pVariable->GetType() != IVariable::INT)
		return 0;

	int value;
	pVariable->Get(value);
	return value;
}

float CEntityObject::GetEntityPropertyFloat( const char *name )
{
	CVarBlock* pProperties = GetProperties();
	if (!pProperties)
		return 0;

	IVariable *pVariable = pProperties->FindVariable(name);
	if (!pVariable)
		return 0;

	if (pVariable->GetType() != IVariable::FLOAT)
		return 0;

	int value;
	pVariable->Get(value);
	return value;
}

CString CEntityObject::GetEntityPropertyString( const char *name )
{
	CString value("");

	CVarBlock* pProperties = GetProperties();
	if (!pProperties)
		return value;

	IVariable *pVariable = pProperties->FindVariable(name);
	if (!pVariable)
		return value;

	if (pVariable->GetType() != IVariable::STRING)
		return value;

	pVariable->Get(value);
	return value;
}

void CEntityObject::DrawExtraLightInfo( DisplayContext &dc )
{
	IObjectManager *objMan = GetIEditor()->GetObjectManager();

	if (objMan)
	{
		if ( objMan->IsLightClass(this) && GetProperties() )
		{
			CString csText("");

			if (GetEntityPropertyBool("bAmbientLight"))
				csText += "A";

			if (!GetEntityPropertyString("texture_Texture").IsEmpty())
				csText += "P";

			if (!GetEntityPropertyString("texture_deferred_cubemap").IsEmpty())
				csText += "C";

			int nLightType = GetEntityPropertyInteger("nCastShadows");
			if (nLightType>0)
				csText+="S";

			float fScale = GetIEditor()->GetViewManager()->GetView(ET_ViewportUnknown)->GetScreenScaleFactor(GetWorldPos());
			Vec3 vDrawPos(GetWorldPos());
			vDrawPos.z +=fScale/25;

			ColorB col(255,255,255);
			dc.SetColor( col );
			dc.DrawTextLabel(vDrawPos,1.3f,csText);
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::Display( DisplayContext &dc )
{
	if (!m_entity)
		return;

	//XFormEntity();
	
	Matrix34 wtm = GetWorldTM();

	COLORREF col = GetColor();
	if (IsFrozen())
		col = dc.GetFreezeColor();

	//Vec3 scale = GetScale();

	dc.PushMatrix( wtm );

	if (m_bDisplayArrow)
	{
		// Show direction arrow.
		Vec3 dir = FORWARD_DIRECTION;
		if (IsFrozen())
			dc.SetFreezeColor();
		else
			dc.SetColor( 1,1,0 );
		dc.DrawArrow( Vec3(0,0,0),FORWARD_DIRECTION*m_helperScale,m_helperScale );
	}

	bool bDisplaySolidBox = (m_bDisplaySolidBBox && gSettings.viewports.bShowTriggerBounds);
	if (IsSelected())
	{
		dc.SetSelectedColor( 0.5f );
		if (!m_visualObject || m_bDisplayBBox || (dc.flags & DISPLAY_2D))
		{
			bDisplaySolidBox = m_bDisplaySolidBBox;
			dc.DrawWireBox( m_box.min,m_box.max );
		}
	}
	else
	{
		if ((m_bDisplayBBox && gSettings.viewports.bShowTriggerBounds) || (dc.flags & DISPLAY_2D))
		{
			dc.SetColor( col,0.3f );
			dc.DrawWireBox( m_box.min,m_box.max );
		}
	}

	// Only display solid BBox if visual object is associated with the entity.
	if (bDisplaySolidBox)
	{
		dc.DepthWriteOff();
		dc.SetColor(col,0.05f);
		dc.DrawSolidBox( m_box.min,m_box.max );
		dc.DepthWriteOn();
	}

	/*
	if (m_entity && m_proximityRadius >= 0)
	{
		float r = m_proximityRadius;
		dc.SetColor( 1,1,0,1 );
		dc.DrawWireBox(  GetPos()-Vec3(r,r,r),GetPos()+Vec3(r,r,r) );
	}
	*/

	// Draw radiuses if present and object selected.
	if (gSettings.viewports.bAlwaysShowRadiuses || IsSelected())
	{
		const Vec3& scale = GetScale();
		float fScale = scale.x; // Ignore matrix scale.
		if (fScale == 0) fScale = 1;
		if (m_innerRadius > 0)
		{
			dc.SetColor( 0,1,0,0.3f );
			dc.DrawWireSphere( Vec3(0,0,0),m_innerRadius/fScale );
		}
		if (m_outerRadius > 0)
		{
			dc.SetColor( 1,1,0,0.8f );
			dc.DrawWireSphere( Vec3(0,0,0),m_outerRadius/fScale );
		}
		if (m_proximityRadius > 0)
		{
			dc.SetColor( 1,1,0,0.8f );
			if( m_bDisplayAbsoluteRadius )
			{
				AffineParts ap;
				//HINT: we need to do this because the entity class does not have a method to get final entity world scale, nice to have one in the future
				ap.SpectralDecompose( wtm );
				dc.DrawWireSphere( Vec3(0,0,0),Vec3( m_proximityRadius / ap.scale.x,m_proximityRadius / ap.scale.y,m_proximityRadius / ap.scale.z) );
			}
			else
				dc.DrawWireSphere( Vec3(0,0,0),m_proximityRadius );
		}
	}

	dc.PopMatrix();

	// Entities themself are rendered by 3DEngine.

	if (m_visualObject)
	{
		/*
		float fScale = dc.view->GetScreenScaleFactor(wtm.GetTranslation()) * 0.04f;
		wtm[0][0] *= fScale; wtm[0][1] *= fScale; wtm[0][2] *= fScale;
		wtm[1][0] *= fScale; wtm[1][1] *= fScale; wtm[1][2] *= fScale;
		wtm[2][0] *= fScale; wtm[2][1] *= fScale; wtm[2][2] *= fScale;
		*/
		Matrix34 tm(wtm);
		float sz = m_helperScale*gSettings.gizmo.helpersScale;
		tm.ScaleColumn( Vec3(sz,sz,sz) );

		SRendParams rp;
    Vec3 color;
		if (IsSelected())
			color = Rgb2Vec(dc.GetSelectedColor());
		else
			color = Rgb2Vec(col);
		rp.AmbientColor = ColorF(color[0], color[1], color[2], 1);
		rp.dwFObjFlags |= FOB_TRANS_MASK;
		rp.fAlpha = 1;
		rp.nDLightMask = 1;//GetIEditor()->Get3DEngine()->GetLightMaskFromPosition(wtm.GetTranslation(),1.f) & 0xFFFF;
		rp.pMatrix = &tm;
		rp.pMaterial = GetIEditor()->GetIconManager()->GetHelperMaterial();
		//rp.nShaderTemplate = EFT_HELPER;
		m_visualObject->Render( rp );
	}

	if (IsSelected())
	{
		if (m_entity)
		{
			IAIObject *pAIObj = m_entity->GetAI();
			if (pAIObj)
				DrawAIInfo( dc,pAIObj );
		}
	}

	if (IsSelected())
		DrawExtraLightInfo(dc);

	/*
	if ((dc.flags & DISPLAY_LINKS) && !m_eventTargets.empty())
	{
		DrawTargets(dc);
	}
	*/
	if ((dc.flags & DISPLAY_HIDENAMES) && gSettings.viewports.bDrawEntityLabels)
	{
		// If labels hidden but we draw entity labels enabled, always draw them.
		CGroup *pGroup = GetGroup();
		if (!pGroup || pGroup->IsOpen())
		{
			DrawLabel( dc,GetWorldPos(),col );
		}
	}

	if (dc.flags & DISPLAY_SELECTION_HELPERS)
		DrawExtraLightInfo(dc);

	if (IsHighlighted() && m_entity && gSettings.viewports.bHighlightMouseOverGeometry)
	{
		SGeometryDebugDrawInfo dd;
		dd.tm = wtm;
		dd.color = ColorB(250,0,250,30);
		dd.lineColor = ColorB(255,255,0,160);
		dd.bExtrude = true;
		m_entity->DebugDraw( dd );
	}

	DrawDefault(dc,col);
	
	IObjectManager* pObjectManager = GetObjectManager();
	if (pObjectManager->IsEntityAssignedToSelectedTerritory(this))
	{	
		Vec3 wp = GetWorldPos();
		AABB box;
		GetBoundBox(box);
		wp.z = box.max.z;

		dc.DrawTextureLabel(
			wp,
			OBJECT_TEXTURE_ICON_SIZEX,
			OBJECT_TEXTURE_ICON_SIZEY,
			GetIEditor()->GetIconManager()->GetIconTexture("Editor/ObjectIcons/T.bmp"),
			DisplayContext::TEXICON_ON_TOP | DisplayContext::TEXICON_ALIGN_BOTTOM);
	}
	if (pObjectManager->IsEntityAssignedToSelectedWave(this))
	{	
		Vec3 wp = GetWorldPos();
		AABB box;
		GetBoundBox(box);
		wp.z = box.max.z;

		dc.DrawTextureLabel(
			wp,
			OBJECT_TEXTURE_ICON_SIZEX,
			OBJECT_TEXTURE_ICON_SIZEY,
			GetIEditor()->GetIconManager()->GetIconTexture("Editor/ObjectIcons/W.bmp"),
			DisplayContext::TEXICON_ON_TOP | DisplayContext::TEXICON_ALIGN_TOP);
	}
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::DrawAIInfo( DisplayContext &dc,IAIObject *aiObj )
{
	assert( aiObj );

	IAIActor * pAIActor = aiObj->CastToIAIActor();
	if(!pAIActor)
		return;
	const AgentParameters &ap =	pAIActor->GetParameters();

	// Draw ranges.
	bool bTerrainCircle = false;
	Vec3 wp = GetWorldPos();
	float z = GetIEditor()->GetTerrainElevation( wp.x,wp.y );
	if (fabs(wp.z-z) < 5)
		bTerrainCircle = true;
/*
		dc.SetColor( RGB(0,255,0) );
		if (bTerrainCircle)
			dc.DrawTerrainCircle( wp,ap.m_fSoundRange,0.2f );
		else
			dc.DrawCircle( wp,ap.m_fSoundRange );

		dc.SetColor( RGB(255,255,0) );
		if (bTerrainCircle)
			dc.DrawTerrainCircle( wp,ap.m_fCommRange,0.2f );
		else
			dc.DrawCircle( wp,ap.m_fCommRange );

		dc.SetColor( RGB(255,0,0) );
		if (bTerrainCircle)
			dc.DrawTerrainCircle( wp,ap.m_fSightRange,0.2f );
		else
			dc.DrawCircle( wp,ap.m_fSightRange );

*/
	dc.SetColor( RGB(255/2,0,0) );
	if (bTerrainCircle)
		dc.DrawTerrainCircle( wp,ap.m_PerceptionParams.sightRange/2,0.2f );
	else
		dc.DrawCircle( wp,ap.m_PerceptionParams.sightRange/2 );

/*
		dc.SetColor( RGB(0,0,255) );
		if (bTerrainCircle)
			dc.DrawTerrainCircle( wp,ap.m_fAttackRange,0.2f );
		else
			dc.DrawCircle( wp,ap.m_fAttackRange );
*/
	//dc.SetColor( 0,1,0,0.3f );		
	//dc.DrawWireSphere( Vec3(0,0,0),m_innerRadius );
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::DrawTargets( DisplayContext &dc )
{
	/*
	BBox box;
	for (int i = 0; i < m_eventTargets.size(); i++)
	{
		CBaseObject *target = m_eventTargets[i].target;
		if (!target)
			return;

		dc.SetColor( 0.8f,0.4f,0.4f,1 );
		GetBoundBox( box );
		Vec3 p1 = 0.5f*Vec3(box.max+box.min);
		target->GetBoundBox( box );
		Vec3 p2 = 0.5f*Vec3(box.max+box.min);

		dc.DrawLine( p1,p2 );

		Vec3 p3 = 0.5f*(p2+p1);

		if (!(dc.flags & DISPLAY_HIDENAMES))
		{
			float col[4] = { 0.8f,0.4f,0.4f,1 };
			dc.renderer->DrawLabelEx( p3+Vec3(0,0,0.3f),1.2f,col,true,true,m_eventTargets[i].event );
		}
	}
	*/
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::Serialize( CObjectArchive &ar )
{
	CBaseObject::Serialize( ar );
	XmlNodeRef xmlNode = ar.node;
	if (ar.bLoading)
	{
		//m_entityId = 0;
		
		//m_physicsState = "";
		// Load
		CString entityClass = m_entityClass;
		m_bLoadFailed = false;

		if (!m_prototype)
			xmlNode->getAttr( "EntityClass",entityClass );
		//xmlNode->getAttr( "EntityId",m_entityId );
		//xmlNode->getAttr( "PhysicsState",m_physicsState );
		m_physicsState = xmlNode->findChild("PhysicsState");

		Vec3 angles;
		// Backward compatability, with FarCry levels.
		if (xmlNode->getAttr( "Angles",angles ))
		{
			angles = DEG2RAD(angles);
			angles.z += gf_PI/2;
			Quat quat;
			quat.SetRotationXYZ(Ang3(angles));
			SetRotation( quat );
		}

		/*
		if (xmlNode->getAttr( "MaterialGUID",m_materialGUID ))
		{
			m_pMaterial = (CMaterial*)GetIEditor()->GetMaterialManager()->FindItem( m_materialGUID );
			if (!m_pMaterial)
			{
				CErrorRecord err;
				err.error.Format( "Material %s for Entity %s not found,",GuidUtil::ToString(m_materialGUID),(const char*)GetName() );
				err.pObject = this;
				err.severity = CErrorRecord::ESEVERITY_WARNING;
				ar.ReportError(err);
				//Warning( "Material %s for Entity %s not found,",GuidUtil::ToString(m_materialGUID),(const char*)GetName() );
			}
			else
			{
				if (m_pMaterial->GetParent())
					SetMaterial( m_pMaterial->GetParent() );
				m_pMaterial->SetUsed();
			}
			UpdateMaterialInfo();
		}
		else
			m_pMaterial = 0;
			*/
		
		// Load Event Targets.
		ReleaseEventTargets();
		XmlNodeRef eventTargets = xmlNode->findChild( "EventTargets" );
		if (eventTargets)
		{
			for (int i = 0; i < eventTargets->getChildCount(); i++)
			{
				XmlNodeRef eventTarget = eventTargets->getChild(i);
				CEntityEventTarget et;
				et.target = 0;
				GUID targetId = GUID_NULL;
				eventTarget->getAttr( "TargetId",targetId );
				eventTarget->getAttr( "Event",et.event );
				eventTarget->getAttr( "SourceEvent",et.sourceEvent );
				m_eventTargets.push_back( et );
				if (targetId != GUID_NULL)
					ar.SetResolveCallback( this,targetId,functor(*this,&CEntityObject::ResolveEventTarget),i );
			}
		}

		XmlNodeRef propsNode;
		XmlNodeRef props2Node = xmlNode->findChild("Properties2");
		if (!m_prototype)
		{
			propsNode = xmlNode->findChild("Properties");
		}

		bool bLoaded = SetClass( entityClass,!ar.bUndo,true,propsNode,props2Node );
		if (ar.bUndo)
		{
			RemoveAllEntityLinks();
			SpawnEntity();
			PostLoad(ar);
		}
	}
	else
	{
		// Saving.
		if (!m_entityClass.IsEmpty() && m_prototype == NULL)
			xmlNode->setAttr( "EntityClass",m_entityClass );

		//if (m_entityId != 0)
			//xmlNode->setAttr( "EntityId",m_entityId );

		//if (!m_physicsState.IsEmpty())
			//xmlNode->setAttr( "PhysicsState",m_physicsState );
		if(m_physicsState)
			xmlNode->addChild(m_physicsState);

		if (!m_prototype)
		{
			//! Save properties.
			if (m_properties)
			{
				XmlNodeRef propsNode = xmlNode->newChild("Properties");
				m_properties->Serialize( propsNode,ar.bLoading );
			}
		}

		//! Save properties.
		if (m_properties2)
		{
			XmlNodeRef propsNode = xmlNode->newChild("Properties2");
			m_properties2->Serialize( propsNode,ar.bLoading );
		}

		// Save Event Targets.
		if (!m_eventTargets.empty())
		{
			XmlNodeRef eventTargets = xmlNode->newChild( "EventTargets" );
			for (int i = 0; i < m_eventTargets.size(); i++)
			{
				CEntityEventTarget &et = m_eventTargets[i];
				GUID targetId = GUID_NULL;
				if (et.target != 0)
					targetId = et.target->GetId();

				XmlNodeRef eventTarget = eventTargets->newChild( "EventTarget" );
				eventTarget->setAttr( "TargetId",targetId );
				eventTarget->setAttr( "Event",et.event );
				eventTarget->setAttr( "SourceEvent",et.sourceEvent );
			}
		}

		// Save Entity Links.
		if (!m_links.empty())
		{
			XmlNodeRef linksNode = xmlNode->newChild( "EntityLinks" );
			for (int i = 0,num = m_links.size(); i < num; i++)
			{
				XmlNodeRef linkNode = linksNode->newChild( "Link" );
				linkNode->setAttr( "TargetId",m_links[i].targetId );
				linkNode->setAttr( "Name",m_links[i].name );
				linkNode->setAttr( "RelRot",m_links[i].relRot );
				linkNode->setAttr( "RelPos",m_links[i].relPos );
			}
		}

		// Save flow graph.
		if (m_pFlowGraph && !m_pFlowGraph->IsEmpty())
		{
			XmlNodeRef graphNode = xmlNode->newChild( "FlowGraph" );
			m_pFlowGraph->Serialize( graphNode, false, &ar );
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::PostLoad( CObjectArchive &ar )
{
	if (m_entity)
	{
		// Force entities to register them-self in sectors.
		// force entity to be registered in terrain sectors again.
		XFormGameEntity();
		BindToParent();
		BindIEntityChilds();
		if (m_pClass)
		{
			//m_pClass->SetCurrentProperties( this );
			m_pClass->SetEventsTable( this );
		}
		if (m_physicsState)
			m_entity->SetPhysicsState( m_physicsState );
	}

	//////////////////////////////////////////////////////////////////////////
	// Load Links.
	RemoveAllEntityLinks();
	XmlNodeRef linksNode = ar.node->findChild( "EntityLinks" );
	if (linksNode)
	{
		CString name;
		GUID targetId;
		for (int i = 0; i < linksNode->getChildCount(); i++)
		{
			XmlNodeRef linkNode = linksNode->getChild(i);
			Quat relRot(IDENTITY);
			Vec3 relPos(IDENTITY);
			linkNode->getAttr( "RelRot",relRot );
			linkNode->getAttr( "RelPos",relPos );
			linkNode->getAttr( "Name",name );
			if (linkNode->getAttr( "TargetId",targetId ))
			{
				GUID newTargetId = ar.ResolveID(targetId);
				AddEntityLink( name,newTargetId,relRot,relPos );
			}
		}
	}

	//////////////////////////////////////////////////////////////////////////
	// Load flow graph after loading of everything.
	XmlNodeRef graphNode = ar.node->findChild( "FlowGraph" );
	if (graphNode)
	{
		if (!m_pFlowGraph)
		{
			SetFlowGraph( GetIEditor()->GetFlowGraphManager()->CreateGraphForEntity( this ) );
		}
		if (m_pFlowGraph && m_pFlowGraph->GetIFlowGraph())
			m_pFlowGraph->Serialize( graphNode, true, &ar );
	}
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::CheckSpecConfig( )
{
	if( m_entity && m_entity->GetAI() && GetMinSpec()!=0)
	{
		CErrorRecord err;
		err.error.Format( "AI entity %s ->> spec dependent",(const char*)GetName() );
		err.pObject = this;
		err.severity = CErrorRecord::ESEVERITY_WARNING;
		GetIEditor()->GetErrorReport()->ReportError(err);
	}
}

//////////////////////////////////////////////////////////////////////////
XmlNodeRef CEntityObject::Export( const CString &levelPath,XmlNodeRef &xmlExportNode )
{
	if (m_bLoadFailed)
		return 0;

	// Do not export entity with bad id.
	if (!m_entityId)
		return XmlHelpers::CreateXmlNode("Temp");

	CheckSpecConfig( );

	// Export entities to entities.ini
	XmlNodeRef objNode = xmlExportNode->newChild( "Entity" );

	objNode->setAttr( "Name",GetName() );

	if (GetMaterial())
		objNode->setAttr( "Material",GetMaterial()->GetName() );

	if (m_prototype)
		objNode->setAttr( "Archetype",m_prototype->GetFullName() );

	Vec3 pos = GetPos(),scale = GetScale();
	Quat rotate = GetRotation();
	if (GetGroup() && GetGroup()->GetParent()
	&& GetGroup()->GetParent()->IsKindOf( RUNTIME_CLASS(CEntityObject) ))
	{
			// Store the parent entity id of the group.
			CEntityObject *parentEntity = (CEntityObject*)(GetGroup()->GetParent());
			if (parentEntity)
			{
				if (gEnv->pEntitySystem->EntitiesUseGUIDs())
					objNode->setAttr( "ParentGuid", ToEntityGuid(parentEntity->GetId()));
				else
					objNode->setAttr( "ParentId",parentEntity->GetEntityId() );
			}
	}
	else if (GetParent())
	{
		if (GetParent()->IsKindOf( RUNTIME_CLASS(CEntityObject) ))
		{
			// Store parent entity id.
			CEntityObject *parentEntity = (CEntityObject*)GetParent();
			if (parentEntity)
			{
				if (gEnv->pEntitySystem->EntitiesUseGUIDs())
					objNode->setAttr( "ParentGuid", ToEntityGuid(parentEntity->GetId()));
				else
					objNode->setAttr( "ParentId",parentEntity->GetEntityId() );
			}
		}
		else
		{
			// Export world coordinates.
			AffineParts ap;
			ap.SpectralDecompose( GetWorldTM() );
			pos = ap.pos;
			rotate = ap.rot;
			scale = ap.scale;
		}
	}

	if (!IsEquivalent(pos,Vec3(0,0,0),0))
		objNode->setAttr( "Pos",pos );

	if (!rotate.IsIdentity())
		objNode->setAttr( "Rotate",rotate );

	if (!IsEquivalent(scale,Vec3(1,1,1),0))
		objNode->setAttr( "Scale",scale );
	
	objNode->setTag( "Entity" );
	objNode->setAttr( "EntityClass",m_entityClass );
	objNode->setAttr( "EntityId",m_entityId );
	objNode->setAttr( "EntityGuid",ToEntityGuid( GetId() ) );

	if (mv_ratioLOD != 100)
		objNode->setAttr( "LodRatio",(int)mv_ratioLOD );

	if (mv_ratioViewDist != 100)
		objNode->setAttr( "ViewDistRatio",(int)mv_ratioViewDist );

	if (!mv_castShadow)
		objNode->setAttr( "CastShadow",false );

	//if (!mv_recvShadow)
		//objNode->setAttr( "RecieveShadow",false );

  if (mv_recvWind)
	{
		objNode->setAttr( "RecvWind",true );

		if (mv_bending > 0.0f)
			objNode->setAttr("bending", mv_bending);
	}

	if(mv_noDecals)
		objNode->setAttr( "NoDecals",true );

	if (mv_outdoor)
		objNode->setAttr( "OutdoorOnly",true );

	if (GetMinSpec() != 0)
		objNode->setAttr( "MinSpec",(uint32)GetMinSpec() );

	uint32 nMtlLayersMask = GetMaterialLayersMask();
	if (nMtlLayersMask != 0)
		objNode->setAttr( "MatLayersMask",nMtlLayersMask );

	if (mv_globalInSW)
		objNode->setAttr( "GlobalInSW", true);

	if (mv_hiddenInGame)
		objNode->setAttr( "HiddenInGame",true );

	if (mv_createdThroughPool)
		objNode->setAttr("CreatedThroughPool", true);

	//if (!m_physicsState.IsEmpty())
		//objNode->setAttr( "PhysicsState",m_physicsState );
	if(m_physicsState)
		objNode->addChild(m_physicsState);

	if (GetLayer()->GetName())
		objNode->setAttr( "Layer",GetLayer()->GetName() );

	// Export Event Targets.
	if (!m_eventTargets.empty())
	{
		XmlNodeRef eventTargets = objNode->newChild( "EventTargets" );
		for (int i = 0; i < m_eventTargets.size(); i++)
		{
			CEntityEventTarget &et = m_eventTargets[i];

			int entityId = 0;
			EntityGUID entityGuid = 0;
			if (et.target)
			{
				if (et.target->IsKindOf( RUNTIME_CLASS(CEntityObject) ))
				{
					entityId = ((CEntityObject*)et.target)->GetEntityId();
					entityGuid = ToEntityGuid(et.target->GetId());
				}
			}

			XmlNodeRef eventTarget = eventTargets->newChild( "EventTarget" );
			//eventTarget->setAttr( "Target",obj->GetName() );
			if (gEnv->pEntitySystem->EntitiesUseGUIDs())
				eventTarget->setAttr( "TargetGuid", entityGuid );
			else
				eventTarget->setAttr( "Target",entityId );
			eventTarget->setAttr( "Event",et.event );
			eventTarget->setAttr( "SourceEvent",et.sourceEvent );
		}
	}

	// Save Entity Links.
	if (!m_links.empty())
	{
		XmlNodeRef linksNode = objNode->newChild( "EntityLinks" );
		for (int i = 0,num = m_links.size(); i < num; i++)
		{
			if (m_links[i].target)
			{
				XmlNodeRef linkNode = linksNode->newChild( "Link" );
				if (gEnv->pEntitySystem->EntitiesUseGUIDs())
					linkNode->setAttr( "TargetGuid", ToEntityGuid(m_links[i].target->GetId()) );
				else
					linkNode->setAttr( "TargetId",m_links[i].target->GetEntityId() );
				linkNode->setAttr( "Name",m_links[i].name );
				linkNode->setAttr( "RelRot",m_links[i].relRot );
				linkNode->setAttr( "RelPos",m_links[i].relPos );
			}
		}
	}

	//! Export properties.
	if (m_properties)
	{
		XmlNodeRef propsNode = objNode->newChild("Properties");
		m_properties->Serialize( propsNode,false );
	}
	//! Export properties.
	if (m_properties2)
	{
		XmlNodeRef propsNode = objNode->newChild("Properties2");
		m_properties2->Serialize( propsNode,false );
	}

	if (objNode != NULL && m_entity != NULL)
	{
		// Export internal entity data.
		m_entity->SerializeXML( objNode,false );
	}

	// Save flow graph.
	if (m_pFlowGraph && !m_pFlowGraph->IsEmpty())
	{
		XmlNodeRef graphNode = objNode->newChild( "FlowGraph" );
		m_pFlowGraph->Serialize( graphNode, false, 0 );
	}

	return objNode;
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::OnEvent( ObjectEvent event )
{
	CBaseObject::OnEvent(event);

	switch (event)
	{
		case EVENT_INGAME:
		case EVENT_OUTOFGAME:
			if (m_entity)
			{
				if (event == EVENT_INGAME)
				{
					if (!m_bCalcPhysics)
						m_entity->ClearFlags(ENTITY_FLAG_IGNORE_PHYSICS_UPDATE);
					// Entity must be hidden when going to game.
					if (m_bVisible)
						m_entity->Hide( mv_hiddenInGame );
				}
				else if (event == EVENT_OUTOFGAME)
				{
					// Entity must be returned to editor visibility state.
					m_entity->Hide( !m_bVisible );
					//m_entity->SetGarbageFlag(false); // If marked as garbage, unmark it.
				}
				XFormGameEntity();
				OnRenderFlagsChange(0);
				/*
				if (!m_physicsState.IsEmpty())
				{
					IPhysicalEntity *physic = m_entity->GetPhysics();
					if (physic)
					{
						const char *str = m_physicsState;
						physic->SetStateFromSnapshotTxt( const_cast<char*>(str),m_physicsState.GetLength() );
						physic->PostSetStateFromSnapshot();
					}
				}
				*/

				if (event == EVENT_OUTOFGAME)
				{
					if (!m_bCalcPhysics)
						m_entity->SetFlags(ENTITY_FLAG_IGNORE_PHYSICS_UPDATE);
				}
			}
			break;
		case EVENT_REFRESH:
			if (m_entity)
			{
				// force entity to be registered in terrain sectors again.

				//-- little hack to force reregistration of entities
				//<<FIXME>> when issue with registration in editor is resolved
				Vec3 pos = GetPos();
				pos.z+=1.f;
				m_entity->SetPos( pos );
				//----------------------------------------------------

				XFormGameEntity();
			}
			break;

		case EVENT_UNLOAD_GEOM:
		case EVENT_UNLOAD_ENTITY:
			if (m_pClass)
				m_pClass = 0;
			if (m_entity)
			{
				UnloadScript();
			}
			break;

		case EVENT_RELOAD_ENTITY:
			GetIEditor()->GetErrorReport()->SetCurrentValidatorObject( this );
			if (m_pClass)
				m_pClass->Reload();
			Reload();
			break;

		case EVENT_RELOAD_GEOM:
			GetIEditor()->GetErrorReport()->SetCurrentValidatorObject( this );
			Reload();
			break;

		case EVENT_PHYSICS_GETSTATE:
			AcceptPhysicsState();
			break;
		case EVENT_PHYSICS_RESETSTATE:
			ResetPhysicsState();
			break;
		case EVENT_PHYSICS_APPLYSTATE:
			if (m_entity&&m_physicsState)
			{
				m_entity->SetPhysicsState( m_physicsState );
			}
			break;

		case EVENT_UPDATE_TRACKGIZMO:
			UpdateTrackGizmo();
			break;

		case EVENT_FREE_GAME_DATA:
			FreeGameData();
			break;

		case EVENT_CONFIG_SPEC_CHANGE:
		{
			IObjectManager *objMan = GetIEditor()->GetObjectManager();
			if (objMan && objMan->IsLightClass(this) )
				OnPropertyChange(NULL);
			break;
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::UpdateTrackGizmo()
{
	bool bNeedTrackGizmo = m_bVisible && m_pAnimNode != 0;
	if (m_trackGizmo != 0 && !bNeedTrackGizmo)
	{
		RemoveGizmo( m_trackGizmo );
		m_trackGizmo = 0;
	}
	if (m_trackGizmo == 0 && bNeedTrackGizmo)
	{
		m_trackGizmo = new CTrackGizmo;
		AddGizmo( m_trackGizmo );
	}
	if (m_trackGizmo)
	{
		m_trackGizmo->SetAnimNode( m_pAnimNode );
	}
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::Reload( bool bReloadScript )
{
	if (!m_pClass || bReloadScript)
		SetClass( m_entityClass,true );
	if (m_entity)
		DeleteEntity();
	SpawnEntity();
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::UpdateVisibility( bool bVisible )
{
	CBaseObject::UpdateVisibility(bVisible);

	bool bVisibleWithSpec = bVisible && !IsHiddenBySpec();
	if (bVisibleWithSpec != m_bVisible)
	{
		m_bVisible = bVisibleWithSpec;

		UpdateTrackGizmo();
	}

	UpdateEntityAndChildrenVisibility();
};

//////////////////////////////////////////////////////////////////////////
void CEntityObject::UpdateEntityAndChildrenVisibility()
{
	if ( m_entity == NULL )
	{
		return;
	}

	bool hide = ! m_bVisible;
	m_entity->Hide( hide );

	for ( int i = 0; i < GetChildCount(); i++ )
	{
		CBaseObject* pChildObject = GetChild( i );
		bool childVisible = ! pChildObject->IsHidden();
		pChildObject->UpdateVisibility( childVisible );
	}
}

/*
class StaticInit
{
public:
	StaticInit()
	{
		Vec3 angles(20,11.51,112);
		Vec3 a1,a2;

		Matrix tm;
		tm.Identity();
		tm.RotateMatrix( angles );
		quaternionf qq( angles.z*PI/180.0f,angles.y*PI/180.0f,angles.x*PI/180.0f );

		float mtx[3][3];
		qq.getmatrix_buf( (float*)mtx );

		Quat q(tm);
		Quat q1;
		q1.SetEulerAngles( angles*PI/180.0f );
		Matrix tm1;
		q1.GetMatrix(tm1);
		a1 = q.GetEulerAngles() * 180.0f/PI;
		a2 = q1.GetEulerAngles() * 180.0f/PI;
	}
};
StaticInit ss;
*/

//////////////////////////////////////////////////////////////////////////
void CEntityObject::DrawDefault( DisplayContext &dc,COLORREF labelColor )
{
	CBaseObject::DrawDefault( dc, labelColor );
	
	bool bDisplaySelectionHelper = false;
	if (m_entity && CanBeDrawn(dc, bDisplaySelectionHelper))
	{
		const Vec3 wp = m_entity->GetWorldPos();

		if ( gEnv->pAISystem )
		{
			ISmartObjectManager* pSmartObjectManager = gEnv->pAISystem->GetSmartObjectManager();
			if (!pSmartObjectManager->ValidateSOClassTemplate(m_entity))
			{
				DrawLabel(dc, wp, RGB(255, 0, 0), 1.f, 4);
			}
			if ( IsSelected() || IsHighlighted() )
			{
				pSmartObjectManager->DrawSOClassTemplate( m_entity );
			}
		}

		// Draw "ghosted" data around the entity's actual position in simulation mode
		if (GetIEditor()->GetGameEngine()->GetSimulationMode())
		{
			if (bDisplaySelectionHelper)
			{
				DrawSelectionHelper( dc,wp,labelColor,0.5f );
			}
			else if (!(dc.flags & DISPLAY_HIDENAMES))
			{
				DrawLabel( dc,wp,labelColor,0.5f );
			}

			DrawTextureIcon( dc,wp,0.5f );
		}
	}
}

void CEntityObject::DrawTextureIcon( DisplayContext &dc,const Vec3 &pos,float alpha )
{
	if ( GetTextureIcon() && ( gSettings.viewports.bShowSizeBasedIcons || gSettings.viewports.bShowIcons ) )
	{
		CEntityScript* pEntityScript = GetScript();

		if (pEntityScript)
		{
			SetDrawTextureIconProperties(dc,pos,alpha);

			int nIconSizeX = OBJECT_TEXTURE_ICON_SIZEX;
			int nIconSizeY = OBJECT_TEXTURE_ICON_SIZEY;

			if ( ( stricmp(pEntityScript->GetName(),"Light") == 0) && gSettings.viewports.bShowSizeBasedIcons )
			{
				float fRadiusModValue = m_proximityRadius;

				if (m_proximityRadius<1)
					fRadiusModValue=1;

				if (m_proximityRadius>50)
					fRadiusModValue=50;

				// Radius=1 is 0.5x icon size, Radius=50 is 2x icon size.
				fRadiusModValue /= 33.3f;
				fRadiusModValue +=0.5f;

				nIconSizeX *= fRadiusModValue;
				nIconSizeY *= fRadiusModValue;
			}

			dc.DrawTextureLabel( GetTextureIconDrawPos(),nIconSizeX,nIconSizeY,GetTextureIcon(),GetTextureIconFlags() );
		} 
	}
}

//////////////////////////////////////////////////////////////////////////
bool CEntityObject::IsInCameraView(const CCamera &camera)
{
	bool bResult = CBaseObject::IsInCameraView(camera);

	if (!bResult && m_entity && GetIEditor()->GetGameEngine()->GetSimulationMode())
	{
		AABB bbox;
		m_entity->GetWorldBounds(bbox);
		bResult = (camera.IsAABBVisible_F( AABB(bbox.min,bbox.max) ));
	}

	return bResult;
}

//////////////////////////////////////////////////////////////////////////
float CEntityObject::GetCameraVisRatio(const CCamera &camera)
{
	float visRatio = CBaseObject::GetCameraVisRatio(camera);

	if (m_entity && GetIEditor()->GetGameEngine()->GetSimulationMode())
	{
		AABB bbox;
		m_entity->GetWorldBounds( bbox );

		float objectHeightSq = max( 1.0f,(bbox.max - bbox.min).GetLengthSquared() );
		float camdistSq = (bbox.min - camera.GetPosition()).GetLengthSquared();
		if (camdistSq > FLT_EPSILON)
			visRatio = max(visRatio, objectHeightSq / camdistSq);
	}

	return visRatio;
}

//////////////////////////////////////////////////////////////////////////
bool CEntityObject::IntersectRectBounds( const AABB &bbox )
{
	bool bResult = CBaseObject::IntersectRectBounds(bbox);

	// Check real entity in simulation mode as well
	if (!bResult && m_entity && GetIEditor()->GetGameEngine()->GetSimulationMode())
	{
		AABB aabb;
		m_entity->GetWorldBounds(aabb);

		bResult = aabb.IsIntersectBox(bbox);
	}

	return bResult;
}

//////////////////////////////////////////////////////////////////////////
bool CEntityObject::IntersectRayBounds( const Ray &ray )
{
	bool bResult = CBaseObject::IntersectRayBounds(ray);

	// Check real entity in simulation mode as well
	if (!bResult && m_entity && GetIEditor()->GetGameEngine()->GetSimulationMode())
	{
		Vec3 tmpPnt;
		AABB aabb;
		m_entity->GetWorldBounds(aabb);

		bResult = Intersect::Ray_AABB( ray,aabb,tmpPnt);
	}

	return bResult;
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::OnNodeHidden( IAnimNode *pAnimNode )
{
	if (!m_pAnimNode)
		return;

	if (m_entity)
	{
		bool bVisChanged = m_bVisible == m_entity->IsHidden();
		if (bVisChanged)
		{
			SetHidden(m_entity->IsHidden());
			bool bUnhideSelected = IsHidden() == false && IsSelected() == true;
			if (bUnhideSelected)
			{
				GetIEditor()->GetObjectManager()->UnselectObject(this);
				GetIEditor()->GetObjectManager()->SelectObject(this);
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::OnNodeAnimated( IAnimNode *pAnimNode )
{
	if (!m_pAnimNode)
		return;

	Vec3 pos = m_pAnimNode->GetPos();
	Vec3 scale = m_pAnimNode->GetScale();
	Quat rotate = m_pAnimNode->GetRotate();

	if (m_pAnimNode->IsBoneLinkTransformEnabled())
	{
		QuatT xform(IDENTITY);
		if (GetFinalTransformFromBoneAttach(xform) == false)
			return;
		pos = xform.t;
		rotate = xform.q;
	}

	SetLocalTM( pos,rotate,scale,TM_ANIMATION );

	if( m_bLight )
		OnNodeAnimatedForLight( pAnimNode );
}

IVariable* CEntityObject::GetLightVariable(const char *name0) const
{
	if( m_properties2 )
	{
		IVariable* pLightProperties = m_properties2->FindVariable("LightProperties_Base");

		if( pLightProperties )
		{
			for( int i = 0; i < pLightProperties->NumChildVars(); ++i )
			{
				IVariable* pChild = pLightProperties->GetChildVar(i);

				if( pChild == NULL )
					continue;

				CString name(pChild->GetName());
				if( name == name0 )
					return pChild;
			}
		}
	}

	return m_properties ? m_properties->FindVariable(name0) : nullptr;
}

void CEntityObject::OnNodeAnimatedForLight(  IAnimNode *pAnimNode )
{
	if( pAnimNode == NULL )
		return;

	IVariable* pRadius = GetLightVariable( "Radius" );
	if( pRadius )
	{
		float	radius(0);
		if(  pAnimNode->GetParamValue( pAnimNode->GetTime(), APARAM_LIGHT_RADIUS, radius ) )
			pRadius->Set(radius);
	}
	else
	{
		assert(0);
	}

	IVariable* pColorGroup = GetLightVariable( "Color" );	
	if( pColorGroup )
	{
		for( int i = 0; i < pColorGroup->NumChildVars(); ++i )
		{
			IVariable* pChild = pColorGroup->GetChildVar(i);

			if( pChild == NULL )
				continue;

			CString name(pChild->GetName());
			if( name == "clrDiffuse" || name == "clrColor" )
			{
				Vec3	diffuseColor(0,0,0);
				if(  pAnimNode->GetParamValue( pAnimNode->GetTime(), APARAM_LIGHT_DIFFUSECOLOR, diffuseColor ) )
				{				
					ColorF fColor(diffuseColor.x/255.0f,diffuseColor.y/255.0f,diffuseColor.z/255.0f);
					if( gEnv->pRenderer->EF_Query( EFQ_sLinearSpaceShadingEnabled ) )
					{
						COLORREF dwColor(RGB( (DWORD)diffuseColor.x, (DWORD)diffuseColor.y, (DWORD)diffuseColor.z ));
						fColor = CCustomColorDialog::GammaToLinear(dwColor);
					}
					pChild->Set(Vec3(fColor.r,fColor.g,fColor.b));
				}
			}
			else if( name == "fDiffuseMultiplier" || name == "fColorMultiplier" )
			{
				float	dffuseMultiplier(0);
				if(  pAnimNode->GetParamValue( pAnimNode->GetTime(), APARAM_LIGHT_DIFFUSEMULTIPLIER, dffuseMultiplier ) )
					pChild->Set(dffuseMultiplier);
			}
			else if( name == "fSpecularMultiplier" )
			{
				float	specularMultiplier(0);
				if(  pAnimNode->GetParamValue( pAnimNode->GetTime(), APARAM_LIGHT_SPECULARMULTIPLIER, specularMultiplier ) )
					pChild->Set(specularMultiplier);
			}
			else if( name == "fHDRDynamic" )
			{
				float	hdrDynamic(0);
				if(  pAnimNode->GetParamValue( pAnimNode->GetTime(), APARAM_LIGHT_HDRDYNAMIC, hdrDynamic ) )
					pChild->Set(hdrDynamic);
			}
			else if( name == "fSpecularPercentage" )
			{
				float	specularPercentage(0);
				if(  pAnimNode->GetParamValue( pAnimNode->GetTime(), APARAM_LIGHT_SPECULARPERCENTAGE, specularPercentage ) )
					pChild->Set(specularPercentage);
			}
		}
	}
	else
	{
		assert(0);
	}
}

//////////////////////////////////////////////////////////////////////////
CString CEntityObject::GetLightAnimation() const
{
	IVariable* pStyleGroup = GetLightVariable( "Style" );	
	if( pStyleGroup )
	{
		for( int i = 0; i < pStyleGroup->NumChildVars(); ++i )
		{
			IVariable* pChild = pStyleGroup->GetChildVar(i);

			if( pChild == NULL )
				continue;

			CString name(pChild->GetName());
			if( name == "lightanimation_LightAnimation" )
			{
				CString lightAnimationName;
				pChild->Get(lightAnimationName);
				return lightAnimationName;
			}
		}
	}

	return "";
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::OnNodeReset( IAnimNode *pAnimNode )
{
	if (!m_pAnimNode)
		return;

	if(gEnv->IsEditing())
	{
		// If the node has an event track, one should also reload the script when the node is reset.
		IAnimTrack *pAnimTrack = m_pAnimNode->GetTrackForParameter(APARAM_EVENT);
		if (pAnimTrack && pAnimTrack->GetNumKeys())
		{
			CEntityScript *script = GetScript();
			script->Reload();
			Reload(true);
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::InvalidateTM( int nWhyFlags )
{
	CBaseObject::InvalidateTM(nWhyFlags);

	bool bBoneLinkTransformedByNode = m_pAnimNode && m_pAnimNode->IsBoneLinkTransformEnabled();
	if (m_trackGizmo && bBoneLinkTransformedByNode)
	{
		QuatT jointXform(IDENTITY);
		if (GetBoneAttachLinkIndex() >= 0)
			GetAttachedBoneTransform(jointXform);
		m_trackGizmo->SetMatrix(Matrix34(jointXform));
	}

	if (nWhyFlags & TM_RESTORE_UNDO) // Can skip updating game object when restoring undo.
		return;

	// If matrix changes.
	if (m_trackGizmo && !bBoneLinkTransformedByNode)
	{
		//m_trackGizmo->SetMatrix( GetWorldTM() );
		if (GetParent())
		{
			m_trackGizmo->SetMatrix( GetParent()->GetWorldTM() );
		}
		else
		{
			Matrix34 tm;
			tm.SetIdentity();
			m_trackGizmo->SetMatrix(tm);
		}
	}

	// Make sure components are correctly calculated.
	const Matrix34 &tm = GetWorldTM();
	QuatT boneLinkXform(IDENTITY);
	if (m_entity)
	{
		int nWhyEntityFlag = 0;
		if (nWhyFlags & TM_ANIMATION)
		{
			nWhyEntityFlag = ENTITY_XFORM_TRACKVIEW;
		}
		else
		{
			nWhyEntityFlag = ENTITY_XFORM_EDITOR;
		}

		int boneAttachLinkIndex = GetBoneAttachLinkIndex();
		if (boneAttachLinkIndex >= 0)
		{
			bool bChangeByUserOrUndo = (nWhyFlags & TM_USER_INPUT) || (nWhyFlags & TM_UNDO);
			if (bChangeByUserOrUndo)
				AdjustBoneAttachLink(boneAttachLinkIndex, boneLinkXform);
		}
		else if (GetParent() && !m_entity->GetParent())
		{
			m_entity->SetWorldTM( tm,nWhyEntityFlag );
		}
		else if (GetLookAt())
		{
			m_entity->SetWorldTM( tm,nWhyEntityFlag );
		}
		else
		{
			if (nWhyFlags & TM_PARENT_CHANGED)
				nWhyEntityFlag |= ENTITY_XFORM_FROM_PARENT;
			m_entity->SetPosRotScale( GetPos(),GetRotation(),GetScale(),nWhyEntityFlag );
      // In case scale has changed, we need to recreate the entity to 
      // ensure that complex physical setup is correctly re-applied after
      // transformation 
      if (nWhyFlags & TM_SCL_CHANGED) 
        Reload(true); 
		}
	}

	if (m_pAnimNode != NULL && !(nWhyFlags & TM_UNDO))
	{
		float time = GetIEditor()->GetAnimation()->GetTime();
		if (!(nWhyFlags & TM_ANIMATION)) // when Matrix invalidation caused by animation itself do not propogate chanegs to anim node.
		{	 
			if (nWhyFlags&TM_POS_CHANGED)
			{
				IAnimTrack *pTrack = m_pAnimNode->GetTrackForParameter(APARAM_POS);
				if (pTrack)
				{
					CUndo::Record( new CUndoTrackObject(pTrack, m_pAnimNode->GetSequence()) );
					if (GetIEditor()->GetAnimation()->IsRecording() == false
						&& m_pAnimNode->IsBoneLinkTransformEnabled() == false)
					{
						// Offset all keys by move amount.
						Vec3 posPrev;
						pTrack->GetValue(time, posPrev);
						Vec3 offset = GetPos() - posPrev;
						pTrack->OffsetKeyPosition(offset);
					}
					if (m_pAnimNode->IsBoneLinkTransformEnabled())
						m_pAnimNode->SetPos( time,boneLinkXform.t );
					else
						m_pAnimNode->SetPos( time,GetPos() );
				}
			}
			if (nWhyFlags&TM_ROT_CHANGED)
			{
				IAnimTrack *pTrack = m_pAnimNode->GetTrackForParameter(APARAM_ROT);
				if (pTrack)
				{
					if (GetIEditor()->GetAnimation()->IsRecording())
						CUndo::Record( new CUndoTrackObject(pTrack, m_pAnimNode->GetSequence()) );
					if (m_pAnimNode->IsBoneLinkTransformEnabled())
						m_pAnimNode->SetRotate( time,boneLinkXform.q );
					else
						m_pAnimNode->SetRotate( time,GetRotation() );
				}
			}
			if (nWhyFlags&TM_SCL_CHANGED)
			{
				IAnimTrack *pTrack = m_pAnimNode->GetTrackForParameter(APARAM_SCL);
				if (pTrack)
				{
					if (GetIEditor()->GetAnimation()->IsRecording())
						CUndo::Record( new CUndoTrackObject(pTrack, m_pAnimNode->GetSequence()) );
					m_pAnimNode->SetScale( time,GetScale() );
				}
			}
		}
	}

	// Update entity and animation node if something changed.

	/*
	if (m_entity && GetParent())
	{
		if (!m_entity->GetParent())
		{
			m_entity->ForceBindCalculation(true);
			m_entity->SetParentLocale( GetParent()->GetWorldTM() );
			XFormGameEntity();
		}
	}
	*/
	m_bEntityXfromValid = false;
}

//////////////////////////////////////////////////////////////////////////
//! Attach new child node.
void CEntityObject::AttachChild( CBaseObject* child,bool bKeepPos )
{
	CBaseObject::AttachChild( child,bKeepPos );
	if (child && child->IsKindOf(RUNTIME_CLASS(CEntityObject)))
	{
		((CEntityObject*)child)->BindToParent();
	}
	else if (child && child->IsKindOf(RUNTIME_CLASS(CGroup)))
	{
		((CGroup*)child)->BindToParent();
	}
 	if (bKeepPos)
	{
		if (m_pAnimNode != NULL)
		{
			float time = GetIEditor()->GetAnimation()->GetTime();
			m_pAnimNode->SetPos( time,GetPos() );
			m_pAnimNode->SetRotate( time,GetRotation() );
			//if (nWhyFlags&TM_SCL_CHANGED)
				//m_pAnimNode->SetScale( time,GetScale() );
		}
	}
}

//! Detach all childs of this node.
void CEntityObject::DetachAll( bool bKeepPos )
{
	//@FIXME: Unbind all childs.
	CBaseObject::DetachAll(bKeepPos);
}

// Detach this node from parent.
void CEntityObject::DetachThis( bool bKeepPos )
{
	UnbindIEntity();
	CBaseObject::DetachThis(bKeepPos);
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::BindToParent()
{
	if (!m_entity)
		return;

	CBaseObject *parent = GetParent();
	if (parent)
	{
		if (parent->IsKindOf(RUNTIME_CLASS(CEntityObject)))
		{
			CEntityObject *parentEntity = (CEntityObject*)parent;

			IEntity *ientParent = parentEntity->GetIEntity();
			if (ientParent)
			{
				XFormGameEntity();
				ientParent->AttachChild( m_entity,IEntity::ATTACHMENT_KEEP_TRANSFORMATION );
				XFormGameEntity();
			}
		}
		else
		{
			/*
			m_entity->ForceBindCalculation(true);
			m_entity->SetParentLocale( GetParent()->GetWorldTM() );
			XFormGameEntity();
			*/
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::BindIEntityChilds()
{
	if (!m_entity)
		return;

	int numChilds = GetChildCount();
	for (int i = 0; i < numChilds; i++)
	{
		CBaseObject *child = GetChild(i);
		if (child && child->IsKindOf(RUNTIME_CLASS(CEntityObject)))
		{
			IEntity *ientChild = ((CEntityObject*)child)->GetIEntity();
			if (ientChild)
			{
				((CEntityObject*)child)->XFormGameEntity();
				m_entity->AttachChild( ientChild );
				((CEntityObject*)child)->XFormGameEntity();
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::UnbindIEntity()
{
	if (!m_entity)
		return;
	
	CBaseObject *parent = GetParent();
	if (parent && parent->IsKindOf(RUNTIME_CLASS(CEntityObject)))
	{
		CEntityObject *parentEntity = (CEntityObject*)parent;
		
		IEntity *ientParent = parentEntity->GetIEntity();
		if (ientParent)
		{
			//m_entity->ForceBindCalculation(false);
			m_entity->DetachThis(IEntity::ATTACHMENT_KEEP_TRANSFORMATION);
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::PostClone( CBaseObject *pFromObject,CObjectCloneContext &ctx )
{
	CBaseObject::PostClone( pFromObject,ctx );
	
	CEntityObject *pFromEntity = (CEntityObject*)pFromObject;
	// Clone event targets.
	if (!pFromEntity->m_eventTargets.empty())
	{
		int numTargets = pFromEntity->m_eventTargets.size();
		for (int i = 0; i < numTargets; i++)
		{
			CEntityEventTarget &et = pFromEntity->m_eventTargets[i];
			CBaseObject *pClonedTarget = ctx.FindClone( et.target );
			if (!pClonedTarget)
				pClonedTarget = et.target; // If target not cloned, link to original target.

			// Add cloned event.
			AddEventTarget( pClonedTarget,et.event,et.sourceEvent,true );
		}
	}

	// Clone links.
	if (!pFromEntity->m_links.empty())
	{
		int numTargets = pFromEntity->m_links.size();
		for (int i = 0; i < numTargets; i++)
		{
			CEntityLink &et = pFromEntity->m_links[i];
			CBaseObject *pClonedTarget = ctx.FindClone( et.target );
			if (!pClonedTarget)
				pClonedTarget = et.target; // If target not cloned, link to original target.

			// Add cloned event.
			if (pClonedTarget)
				AddEntityLink( et.name,pClonedTarget->GetId() );
			else
				AddEntityLink( et.name,GUID_NULL );
		}
	}


	if (m_pFlowGraph)
	{
		m_pFlowGraph->PostClone( pFromObject, ctx);
	}
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::ResolveEventTarget( CBaseObject *object,unsigned int index )
{
	// Find targetid.
	assert( index >= 0 && index < m_eventTargets.size() );
	if (object)
		object->AddEventListener( functor(*this,&CEntityObject::OnEventTargetEvent) );
	m_eventTargets[index].target = object;
	if (!m_eventTargets.empty() && m_pClass != 0)
		m_pClass->SetEventsTable( this );

	// Make line gizmo.
	if (!m_eventTargets[index].pLineGizmo && object)
	{
		CLineGizmo *pLineGizmo = new CLineGizmo;
		pLineGizmo->SetObjects( this,object );
		pLineGizmo->SetColor( Vec3(0.8f,0.4f,0.4f),Vec3(0.8f,0.4f,0.4f) );
		pLineGizmo->SetName( m_eventTargets[index].event );
		AddGizmo( pLineGizmo );
		m_eventTargets[index].pLineGizmo = pLineGizmo;
	}
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::RemoveAllEntityLinks()
{
	while (!m_links.empty())
		RemoveEntityLink( m_links.size()-1);
	m_links.clear();
	SetModified(false);
}

//////////////////////////////////////////////////////////////////////////
int CEntityObject::GetBoneAttachLinkIndex() const
{
	for (size_t i=0; i<m_links.size(); ++i)
	{
		if (IEntityLink::IsBoneAttachLinkName(m_links[i].name))
			return i;
	}
	return -1;
}

//////////////////////////////////////////////////////////////////////////
bool CEntityObject::GetBoneAttachTransforms(QuatT& parentXform, QuatT& relXform) const
{
	int boneAttachLinkIndex = GetBoneAttachLinkIndex();
	if (boneAttachLinkIndex < 0)
		return false;

	const CEntityLink& link = const_cast<CEntityObject*>(this)->GetEntityLink(boneAttachLinkIndex);
	QuatT worldXform(GetWorldTM());
	relXform.t = link.relPos;
	relXform.q = link.relRot;
	parentXform = worldXform * relXform.GetInverted();

	return true;
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::ReleaseEventTargets()
{
	while (!m_eventTargets.empty())
		RemoveEventTarget( m_eventTargets.size()-1,false );
	m_eventTargets.clear();
	SetModified(false);
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::OnEventTargetEvent( CBaseObject *target,int event )
{
	// When event target is deleted.
	if (event == CBaseObject::ON_DELETE)
	{
		// Find this target in events list and remove.
		int numTargets = m_eventTargets.size();
		for (int i = 0; i < numTargets; i++)
		{
			if (m_eventTargets[i].target == target)
			{
				RemoveEventTarget( i );
				numTargets = m_eventTargets.size();
				i--;
			}
		}
		numTargets = m_links.size();
		for (int i = 0; i < numTargets; i++)
		{
			if (m_links[i].target == target)
			{
				RemoveEntityLink(i);
				numTargets = m_eventTargets.size();
				i--;
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
int CEntityObject::AddEventTarget( CBaseObject *target,const CString &event,const CString &sourceEvent,bool bUpdateScript )
{
	StoreUndo( "Add EventTarget" );
	CEntityEventTarget et;
	et.target = target;
	et.event = event;
	et.sourceEvent = sourceEvent;

	// Assign event target.
	if (et.target)
		et.target->AddEventListener( functor(*this,&CEntityObject::OnEventTargetEvent) );

	if (target)
	{
		// Make line gizmo.
		CLineGizmo *pLineGizmo = new CLineGizmo;
		pLineGizmo->SetObjects( this,target );
		pLineGizmo->SetColor( Vec3(0.8f,0.4f,0.4f),Vec3(0.8f,0.4f,0.4f) );
		pLineGizmo->SetName( event );
		AddGizmo( pLineGizmo );
		et.pLineGizmo = pLineGizmo;
	}

	m_eventTargets.push_back( et );

	// Update event table in script.
	if (bUpdateScript && m_pClass != 0)
		m_pClass->SetEventsTable( this );

	SetModified(false);
	return m_eventTargets.size()-1;
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::RemoveEventTarget( int index,bool bUpdateScript )
{
	if (index >= 0 && index < m_eventTargets.size())
	{
		StoreUndo( "Remove EventTarget" );

		if (m_eventTargets[index].pLineGizmo)
		{
			RemoveGizmo( m_eventTargets[index].pLineGizmo );
		}

		if (m_eventTargets[index].target)
			m_eventTargets[index].target->RemoveEventListener( functor(*this,&CEntityObject::OnEventTargetEvent) );
		m_eventTargets.erase( m_eventTargets.begin()+index );

		// Update event table in script.
		if (bUpdateScript && m_pClass != 0 && m_entity != 0)
			m_pClass->SetEventsTable( this );

		SetModified(false);
	}
}

//////////////////////////////////////////////////////////////////////////
int CEntityObject::AddEntityLink( const CString &name,GUID targetEntityId,const Quat& relRot,const Vec3& relPos )
{
	CEntityObject *target = 0;
	if (targetEntityId != GUID_NULL)
	{
		CBaseObject *pObject = FindObject(targetEntityId);
		if (pObject && pObject->IsKindOf(RUNTIME_CLASS(CEntityObject)))
		{
			target = (CEntityObject*)pObject;
		}
	}

	StoreUndo( "Add EntityLink" );

	CLineGizmo *pLineGizmo = 0;
	
	// Assign event target.
	if (target)
	{
		target->AddEventListener( functor(*this,&CEntityObject::OnEventTargetEvent) );

		// Make line gizmo.
		pLineGizmo = new CLineGizmo;
		if (IEntityLink::IsBoneAttachLinkName(name))
		{	
			int boneAttachLinkIndex = GetBoneAttachLinkIndex();
			if (boneAttachLinkIndex >= 0)	// If a bone attach link already exists, remove it first.
				RemoveEntityLink(boneAttachLinkIndex);
			pLineGizmo->SetObjects( this,target,name.Right(name.GetLength()-strlen(BONE_ATTACH_ENTITY_LINK_PREFIX)) );
			pLineGizmo->SetColor( Vec3(0.4f,0.0f,1.0f),Vec3(0.0f,0.0f,1.0f) );
		}
		else
		{
			pLineGizmo->SetObjects( this,target );
			pLineGizmo->SetColor( Vec3(0.4f,1.0f,0.0f),Vec3(0.0f,1.0f,0.0f) );
		}
		pLineGizmo->SetName( name );
		AddGizmo( pLineGizmo );
	}

	CEntityLink lnk;
	lnk.targetId = targetEntityId;
	lnk.target = target;
	lnk.name = name;
	lnk.pLineGizmo = pLineGizmo;
	lnk.relRot = relRot;
	lnk.relPos = relPos;
	m_links.push_back( lnk );

	if (m_entity != NULL && target != NULL)
	{
		// Add link to entity itself.
		m_entity->AddEntityLink( name,target->GetEntityId(),0,relRot,relPos );
		// tell the target about the linkage
		target->EntityLinked(name,GetId());
		SW_ON_ADD_LINK(this, target);
	}

	SetModified(false);

	return m_links.size()-1;
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::RemoveEntityLink( int index )
{
	if (index >= 0 && index < m_links.size())
	{
		StoreUndo( "Remove EntityLink" );

		SW_ON_REMOVE_LINK(this, m_links[index].target);

		if (m_links[index].pLineGizmo)
		{
			RemoveGizmo( m_links[index].pLineGizmo );
		}

		if (m_links[index].target)
			m_links[index].target->RemoveEventListener( functor(*this,&CEntityObject::OnEventTargetEvent) );
		m_links.erase( m_links.begin()+index );

		UpdateIEntityLinks();

		SetModified(false);
	}
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::RenameEntityLink( int index,const CString &newName )
{
	if (index >= 0 && index < m_links.size())
	{
		StoreUndo( "Rename EntityLink" );

		if (m_links[index].pLineGizmo)
		{
			m_links[index].pLineGizmo->SetName(newName);
		}

		m_links[index].name = newName;

		UpdateIEntityLinks();

		SetModified(false);
	}
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::UpdateIEntityLinks( bool bCallOnPropertyChange )
{
	if (m_entity)
	{
		m_entity->RemoveAllEntityLinks();
		for (int i = 0,num = m_links.size(); i < num; i++)
		{
			if (m_links[i].target)
				m_entity->AddEntityLink(m_links[i].name,m_links[i].target->GetEntityId(), 0,
																m_links[i].relRot, m_links[i].relPos);
		}

		if (bCallOnPropertyChange)
			m_pClass->CallOnPropertyChange(m_entity);
	}
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::OnEntityFlagsChange( IVariable *var )
{
	if (m_entity)
	{
		int flags = m_entity->GetFlags();

		if (mv_castShadow)
			flags |= ENTITY_FLAG_CASTSHADOW;
		else
			flags &= ~ENTITY_FLAG_CASTSHADOW;

		//if (mv_recvShadow)
			//flags |= ENTITY_FLAG_RECVSHADOW;
		//else
			//flags &= ~ENTITY_FLAG_RECVSHADOW;

		if (mv_outdoor)
			flags |= ENTITY_FLAG_OUTDOORONLY;
		else
			flags &= ~ENTITY_FLAG_OUTDOORONLY;

    if (mv_recvWind)
		{
      flags |= ENTITY_FLAG_RECVWIND;

			IEntityRenderProxy *pRenderProxy = (IEntityRenderProxy*)m_entity->GetProxy(ENTITY_PROXY_RENDER);
			if (pRenderProxy)
			{
				IRenderNode *pRenderNode = pRenderProxy->GetRenderNode();
				if(pRenderNode)
				{
					pRenderNode->SetBending(mv_bending);
				}
			}
		}
    else
      flags &= ~ENTITY_FLAG_RECVWIND;

    if (mv_noDecals)
      flags |= ENTITY_FLAG_NO_DECALNODE_DECALS;
    else
      flags &= ~ENTITY_FLAG_NO_DECALNODE_DECALS;

		m_entity->SetFlags( flags );
	}
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::OnRenderFlagsChange( IVariable *var )
{
	if (m_entity)
	{
		IEntityRenderProxy *pRenderProxy = (IEntityRenderProxy*)m_entity->GetProxy(ENTITY_PROXY_RENDER);
		if (pRenderProxy)
		{
			IRenderNode *pRenderNode = pRenderProxy->GetRenderNode();
			if (pRenderNode)
			{
				pRenderNode->SetLodRatio(mv_ratioLOD);

				// With Custom view dist ratio it is set by code not UI.
				if (!(m_entity->GetFlags() & ENTITY_FLAG_CUSTOM_VIEWDIST_RATIO))
					pRenderNode->SetViewDistRatio(mv_ratioViewDist);
				else
				{
					// Disable UI for view distance ratio.
					mv_ratioViewDist.SetFlags( mv_ratioViewDist.GetFlags()|IVariable::UI_DISABLED );
				}

				int nRndFlags = pRenderNode->GetRndFlags();
				if (mv_recvWind)
				{
					pRenderNode->SetBending(mv_bending);
					nRndFlags |= ERF_RECVWIND;
				}
				else
				{
					nRndFlags &= ~ERF_RECVWIND;
				}

				if (mv_noDecals)
					nRndFlags |= ERF_NO_DECALNODE_DECALS;
				else
					nRndFlags &= ~ERF_NO_DECALNODE_DECALS;

				pRenderNode->SetRndFlags( nRndFlags );
				pRenderNode->SetMinSpec( GetMinSpec() );
			}
			//// Set material layer flags..
			uint8 nMaterialLayersFlags = GetMaterialLayersMask();
			pRenderProxy->SetMaterialLayersMask( nMaterialLayersFlags );
		}

		SetModified(false);
	}
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::OnRadiusChange( IVariable *var )
{
	var->Get(m_proximityRadius);
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::OnInnerRadiusChange( IVariable *var )
{
	var->Get(m_innerRadius);
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::OnOuterRadiusChange( IVariable *var )
{
	var->Get(m_outerRadius);
}

//////////////////////////////////////////////////////////////////////////
CVarBlock* CEntityObject::CloneProperties( CVarBlock *srcProperties )
{
	assert( srcProperties );

	CVarBlock *properties = srcProperties->Clone(true);

	//@FIXME Hack to dispay radiuses of properties.
	// wires properties from param block, to this entity internal variables.
	IVariable *var = 0;
	var = properties->FindVariable( "Radius",false );
	if (var && (var->GetType() == IVariable::FLOAT || var->GetType() == IVariable::INT))
	{
		var->Get(m_proximityRadius);
		var->AddOnSetCallback( functor(*this,&CEntityObject::OnRadiusChange) );
	}
	else 
	{
		var = properties->FindVariable( "radius",false );
		if (var && (var->GetType() == IVariable::FLOAT || var->GetType() == IVariable::INT))
		{
			var->Get(m_proximityRadius);
			var->AddOnSetCallback( functor(*this,&CEntityObject::OnRadiusChange) );
		}
	}
	
	var = properties->FindVariable( "InnerRadius",false );
	if (var && (var->GetType() == IVariable::FLOAT || var->GetType() == IVariable::INT))
	{
		var->Get(m_innerRadius);
		var->AddOnSetCallback( functor(*this,&CEntityObject::OnInnerRadiusChange) );
	}
	var = properties->FindVariable( "OuterRadius",false );
	if (var && (var->GetType() == IVariable::FLOAT || var->GetType() == IVariable::INT))
	{
		var->Get(m_outerRadius);
		var->AddOnSetCallback( functor(*this,&CEntityObject::OnOuterRadiusChange) );
	}

	IVariable* pProjection = properties->FindVariable( "Projection" );
	if ( pProjection )
	{
		for( int i = 0; i < pProjection->NumChildVars(); ++i )
		{
			IVariable* pChild = pProjection->GetChildVar(i);
			if (!pChild)
				continue;

			CString varName(pChild->GetName());
			if ( varName == "bBoxProject" && pChild->GetType() == IVariable::BOOL )
			{
				int value;
				pChild->Get(value);
				m_bBoxProjectedCM = value;
				pChild->AddOnSetCallback( functor( *this, &CEntityObject::OnBoxProjectionChange ) );		
			}
			else if ( varName == "fBoxWidth" && ( pChild->GetType() == IVariable::FLOAT || pChild->GetType() == IVariable::INT ) )
			{
				pChild->Get( m_fBoxWidth );
				pChild->AddOnSetCallback( functor( *this, &CEntityObject::OnBoxWidthChange ) );		
			}
			else if ( varName == "fBoxHeight" && ( pChild->GetType() == IVariable::FLOAT || pChild->GetType() == IVariable::INT ) )
			{
				pChild->Get( m_fBoxHeight );
				pChild->AddOnSetCallback( functor( *this, &CEntityObject::OnBoxHeightChange ) );		
			}
			else if ( varName == "fBoxLength" && ( pChild->GetType() == IVariable::FLOAT || pChild->GetType() == IVariable::INT ) )
			{
				pChild->Get( m_fBoxLength );
				pChild->AddOnSetCallback( functor( *this, &CEntityObject::OnBoxLengthChange ) );	
			}
		}
	}


	// Each property must have callback to our OnPropertyChange.
	properties->AddOnSetCallback( functor(*this,&CEntityObject::OnPropertyChange) );

	return properties;
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::OnBoxProjectionChange( IVariable *var )
{
	int value;
	var->Get( value );
	m_bBoxProjectedCM = value;
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::OnBoxWidthChange( IVariable *var )
{
	var->Get(m_fBoxWidth);
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::OnBoxHeightChange( IVariable *var )
{
	var->Get(m_fBoxHeight);
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::OnBoxLengthChange( IVariable *var )
{
	var->Get(m_fBoxLength);
}
void CEntityObject::OnLoadFailed()
{
	m_bLoadFailed = true;

	CErrorRecord err;
	err.error.Format( "Entity %s Failed to Spawn (Script: %s)",(const char*)GetName(),(const char*)m_entityClass );
	err.pObject = this;
	GetIEditor()->GetErrorReport()->ReportError(err);
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::SetMaterial( CMaterial *mtl )
{
	CBaseObject::SetMaterial(mtl);
	UpdateMaterialInfo();
}

//////////////////////////////////////////////////////////////////////////
CMaterial* CEntityObject::GetRenderMaterial() const
{
	if (GetMaterial())
		return GetMaterial();
	if (m_entity)
	{
		IEntityRenderProxy *pRenderProxy = (IEntityRenderProxy*)m_entity->GetProxy(ENTITY_PROXY_RENDER);
		if (pRenderProxy)
		{
			return GetIEditor()->GetMaterialManager()->FromIMaterial( pRenderProxy->GetRenderMaterial() );
		}
	}
	return NULL;
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::UpdateMaterialInfo()
{
	if (m_entity)
	{
		if (GetMaterial())
			m_entity->SetMaterial(GetMaterial()->GetMatInfo());
		else
			m_entity->SetMaterial(0);

		IEntityRenderProxy *pRenderProxy = (IEntityRenderProxy*)m_entity->GetProxy(ENTITY_PROXY_RENDER);
		if (pRenderProxy)
		{
			// Set material layer flags on entity.
			uint8 nMaterialLayersFlags = GetMaterialLayersMask();
			pRenderProxy->SetMaterialLayersMask( nMaterialLayersFlags );
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::SetMaterialLayersMask( uint32 nLayersMask )
{
	__super::SetMaterialLayersMask(nLayersMask);
	UpdateMaterialInfo();
}


//////////////////////////////////////////////////////////////////////////
void CEntityObject::SetMinSpec( uint32 nMinSpec )
{
	__super::SetMinSpec(nMinSpec);
	OnRenderFlagsChange(0);
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::AcceptPhysicsState()
{
	if (m_entity)
	{
		StoreUndo( "Accept Physics State" );

		// [Anton] - StoreUndo sends EVENT_AFTER_LOAD, which forces position and angles to editor's position and
		// angles, which are not updated with the physics value
		SetWorldTM( m_entity->GetWorldTM() );

		IPhysicalEntity *physic = m_entity->GetPhysics();
		if (!physic && m_entity->GetCharacter(0)) // for ropes
			physic = m_entity->GetCharacter(0)->GetISkeletonPose()->GetCharacterPhysics(0);
		if (physic && (physic->GetType()==PE_ARTICULATED || physic->GetType()==PE_ROPE))
		{
			/*
			// Only get state snapshot for articulated characters.
			char str[4096*4];
			int len = physic->GetStateSnapshotTxt( str,sizeof(str) );
			if (len > sizeof(str)-1)
				len = sizeof(str)-1;
			str[len] = 0;
			m_physicsState = str;
			/**/

			IXmlSerializer * pSerializer = GetISystem()->GetXmlUtils()->CreateXmlSerializer();
			if(pSerializer)
			{
				m_physicsState = GetISystem()->CreateXmlNode( "PhysicsState" );
				ISerialize * ser = pSerializer->GetWriter(m_physicsState);
				physic->GetStateSnapshot(ser);
				pSerializer->Release();
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::ResetPhysicsState()
{
	if (m_entity)
	{
		//StoreUndo( "Reset Physics State" );
		//m_physicsState = "";
		m_physicsState = 0;
		m_entity->SetPhysicsState( m_physicsState );
		Reload();
	}
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::SetHelperScale( float scale )
{
	bool bChanged = m_helperScale != scale;
	m_helperScale = scale;
	if (bChanged)
	{
		CalcBBox();
		SW_ON_OBJ_MOD(this);
	}
}

//////////////////////////////////////////////////////////////////////////
//! Analyze errors for this object.
void CEntityObject::Validate( CErrorReport *report )
{
	CBaseObject::Validate( report );
	
	if (!m_entity && !m_entityClass.IsEmpty())
	{
		CErrorRecord err;
		err.error.Format( "Entity %s Failed to Spawn (Script: %s)",(const char*)GetName(),(const char*)m_entityClass );
		err.pObject = this;
		report->ReportError(err);
		return;
	}

	if (!m_entity)
		return;

	int slot;
	
	// Check Entity.
	int numObj = m_entity->GetSlotCount();
	for (slot = 0; slot < numObj; slot++)
	{
		IStatObj *pObj = m_entity->GetStatObj(slot);
		if (!pObj)
			continue;

		if (pObj->IsDefaultObject())
		{
			//const char *filename = obj.object->GetFileName();
			// File Not found.
			CErrorRecord err;
			err.error.Format( "Geometry File in Slot %d for Entity %s not found",slot,(const char*)GetName() );
			//err.file = filename;
			err.pObject = this;
			err.flags = CErrorRecord::FLAG_NOFILE;
			report->ReportError(err);
		}
	}
	/*
	IEntityCharacter *pIChar = m_entity->GetCharInterface();
	pIChar->GetCharacter(0);
	for (slot = 0; slot < MAX_ANIMATED_MODELS; slot++)
	{
		ICharacterInstance *pCharacter = pIChar->GetCharacter(slot);
		pCharacter->IsDe
	}
	*/
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::GatherUsedResources( CUsedResources &resources )
{
	CBaseObject::GatherUsedResources( resources );
	if (m_properties)
		m_properties->GatherUsedResources( resources );
	if (m_properties2)
		m_properties2->GatherUsedResources( resources );
	if (m_prototype != NULL && m_prototype->GetProperties())
		m_prototype->GetProperties()->GatherUsedResources( resources );

	if (m_entity)
	{
		IEntityRenderProxy *pRenderProxy = (IEntityRenderProxy*)m_entity->GetProxy(ENTITY_PROXY_RENDER);
		if (pRenderProxy)
		{
			IMaterial *pMtl = pRenderProxy->GetRenderMaterial();
			CMaterialManager::GatherResources(pMtl,resources);
		}
		SEntitySlotInfo slot;
		if (m_entity->GetSlotInfo(0,slot))
		{
			// Maybe patricle.
			if (slot.pParticleEmitter)
			{
				//slot.pParticleEmitter->GetSpawnParams()
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
bool CEntityObject::IsSimilarObject( CBaseObject *pObject )
{
	if (pObject->GetClassDesc() == GetClassDesc() && pObject->GetRuntimeClass() == GetRuntimeClass())
	{
		CEntityObject *pEntity = (CEntityObject*)pObject;
		if (m_entityClass == pEntity->m_entityClass && 
				m_proximityRadius == pEntity->m_proximityRadius &&
				m_innerRadius == pEntity->m_innerRadius &&
				m_outerRadius == pEntity->m_outerRadius)
		{
			return true;
		}
	}
	return false;
}

//////////////////////////////////////////////////////////////////////////
bool CEntityObject::CreateFlowGraphWithGroupDialog()
{
	if (m_pFlowGraph)
		return false;

	CUndo undo( "Create Flow graph" );

	CFlowGraphManager* pFlowGraphManager = GetIEditor()->GetFlowGraphManager();
	std::set<CString> groupsSet;
	pFlowGraphManager->GetAvailableGroups(groupsSet);

	CString groupName;
	bool bDoNewGroup  = true;
	bool bCreateGroup = false;
	bool bDoNewGraph  = false;

	if (groupsSet.size() > 0)
	{
		std::vector<CString> groups;
		groups.push_back (CString(_T("<None>")));
		groups.insert (groups.end(), groupsSet.begin(), groupsSet.end());

		CGenericSelectItemDialog gtDlg(m_panel);
		gtDlg.SetTitle(_T("Choose Group for the Flow Graph"));
		gtDlg.SetItems(groups);
		gtDlg.AllowNew(true);
		switch (gtDlg.DoModal())
		{
		case IDOK:
			groupName = gtDlg.GetSelectedItem();
			bCreateGroup = true;
			bDoNewGroup  = false;
			bDoNewGraph = true;
			break;
		case IDNEW:
			// bDoNewGroup  = true; // is true anyway
			break;
		default:
			bDoNewGroup = false;
			break;
		}
	}

	if (bDoNewGroup)
	{
		CStringDlg dlg( _T("Enter Group Name for the Flow Graph") );
		dlg.SetString(_T("<None>"));
		if (dlg.DoModal() == IDOK)
		{
			bCreateGroup = true;
			groupName = dlg.GetString();
		}
	}

	if (bCreateGroup)
	{
		if (groupName == _T("<None>"))
			groupName.Empty();
		bDoNewGraph = true;
		OpenFlowGraph( groupName );
		if (m_panel)
		{
			m_panel->SetEntity( this );
		}
	}
	return bDoNewGraph;
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::SetFlowGraph( CFlowGraph *pGraph )
{
	if (pGraph != m_pFlowGraph)
	{
		if (m_pFlowGraph)
			m_pFlowGraph->Release();
		m_pFlowGraph = pGraph;
		if (m_pFlowGraph)
		{
			m_pFlowGraph->SetEntity(this, true); // true -> re-adjust graph entity nodes
			m_pFlowGraph->AddRef();

			if (m_entity)
			{
				IEntityFlowGraphProxy *pFlowGraphProxy = (IEntityFlowGraphProxy*)m_entity->CreateProxy(ENTITY_PROXY_FLOWGRAPH);
				pFlowGraphProxy->SetFlowGraph( m_pFlowGraph->GetIFlowGraph() );
			}
		}
		SetModified(false);
	}
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::OpenFlowGraph( const CString &groupName )
{
	if (!m_pFlowGraph)
	{
		StoreUndo("Create Flow Graph");
		//m_pIFlowGraph = GetIEditor()->GetGameEngine()->GetIFlowSystem()->CreateFlowGraph();
		//m_pIFlowGraph->AddRef();
		//m_pIFlowGraph->SetDefaultEntity( GetEntityId() );
		SetFlowGraph( GetIEditor()->GetFlowGraphManager()->CreateGraphForEntity( this, groupName ) );
	}
	GetIEditor()->GetFlowGraphManager()->OpenView( m_pFlowGraph );
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::RemoveFlowGraph( bool bInitFlowGraph )
{
	if (m_pFlowGraph)
	{
		StoreUndo("Remove Flow Graph");
		GetIEditor()->GetFlowGraphManager()->UnregisterAndResetView(m_pFlowGraph, this, bInitFlowGraph);
		m_pFlowGraph->Release();
		m_pFlowGraph = 0;

		if (m_entity)
		{
			IEntityFlowGraphProxy *pFlowGraphProxy = (IEntityFlowGraphProxy*)m_entity->GetProxy(ENTITY_PROXY_FLOWGRAPH);
			if (pFlowGraphProxy)
				pFlowGraphProxy->SetFlowGraph( 0 );
		}

		SetModified(false);
	}
	//GetIEditor()->GetFlowGraphManager()->OpenView( m_pFlowGraph );
}

//////////////////////////////////////////////////////////////////////////
CString CEntityObject::GetSmartObjectClass() const
{
	CString soClass;
	if (m_properties)
	{
		IVariable* pVar = m_properties->FindVariable( "soclasses_SmartObjectClass" );
		if (pVar)
			pVar->Get(soClass);
	}
	return soClass;
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::SetSoundEventSpotSphere()
{
	if(m_properties)
	{
		IVariable const* const pVarName(m_properties->FindVariable("soundName", false));
		if(pVarName)
		{
			m_outerRadius = 0.0f;
			if(!pVarName->GetDisplayValue().IsEmpty())
			{
				_smart_ptr<ISound> const pSound(gEnv->pSoundSystem->CreateSound(pVarName->GetDisplayValue(), 0));
				if(pSound)
				{
					m_outerRadius = pSound->GetMaxDistance();
					pSound->Stop();
				}
			}

			if(s_propertiesPanel)
				s_propertiesPanel->UpdateVarBlock(m_properties);
		}
	}
}

//////////////////////////////////////////////////////////////////////////
IRenderNode*  CEntityObject::GetEngineNode() const
{
	if (m_entity)
	{
		IEntityRenderProxy *pRenderProxy = (IEntityRenderProxy*)m_entity->GetProxy(ENTITY_PROXY_RENDER);
		if (pRenderProxy)
		{
			return pRenderProxy->GetRenderNode();
		}
	}

	return NULL;
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::OnMenuCreateFlowGraph()
{
	if (CreateFlowGraphWithGroupDialog())
		OpenFlowGraph("");
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::OnMenuFlowGraphOpen(CFlowGraph* pFlowGraph)
{
	GetIEditor()->GetFlowGraphManager()->OpenView( pFlowGraph );

	CWnd* pWnd = GetIEditor()->FindView( "Flow Graph" );
	if ( pWnd && pWnd->IsKindOf(RUNTIME_CLASS(CHyperGraphDialog)) )
	{
		CHyperGraphDialog* pHGDlg = (CHyperGraphDialog*) pWnd;
		CFlowGraphSearchCtrl* pSC = pHGDlg->GetSearchControl();
		if (pSC)
		{
			CFlowGraphSearchOptions* pOpts = CFlowGraphSearchOptions::GetSearchOptions();
			pOpts->m_bIncludeEntities = true;
			pOpts->m_findSpecial = CFlowGraphSearchOptions::eFLS_None;
			pOpts->m_LookinIndex = CFlowGraphSearchOptions::eFL_Current;
			pSC->Find(GetName(), false, true, true);
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::OnMenuScriptEvent(int eventIndex)
{
	CEntityScript *pScript = GetScript();
	if (pScript && GetIEntity())
		pScript->SendEvent( GetIEntity(), pScript->GetEvent(eventIndex));
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::OnMenuOpenTrackView(IAnimNode* pAnimNode)
{
	if(GetIEditor()->FindView("Track View") == NULL)
		GetIEditor()->OpenView("Track View");

	CWnd* pWnd = GetIEditor()->FindView("Track View");
	if ( pWnd && pWnd->IsKindOf(RUNTIME_CLASS(CTrackViewDialog)) )
	{
		CTrackViewDialog* pTVDlg = (CTrackViewDialog*) pWnd;
		pTVDlg->SetCurrentSequence(pAnimNode->GetSequence());
		pTVDlg->SnapToNode(pAnimNode->GetName());
	}				
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::OnMenuReloadScripts()
{
	CEntityScript *pScript = GetScript();
	if (pScript)
		pScript->Reload();
	Reload(true);

	SEntityEvent event;
	event.event = ENTITY_EVENT_RELOAD_SCRIPT;
	gEnv->pEntitySystem->SendEventToAll( event );
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::OnMenuReloadAllScripts()
{
	CBaseObjectsArray allObjects;
	GetIEditor()->GetObjectManager()->GetObjects(allObjects);
	CBaseObjectsArray::iterator itObject, end;
	for (itObject = allObjects.begin(), end = allObjects.end(); itObject != end; ++itObject)
	{
		CBaseObject *pObject = *itObject;
		if (pObject && pObject->IsKindOf(RUNTIME_CLASS(CEntityObject)))
		{
			CEntityObject *pThisEntity = (CEntityObject*)pObject;
			if (pThisEntity && pThisEntity->IsSameClass(this))
			{
				CEntityScript *pScript = pThisEntity->GetScript();
				if (pScript)
				{
					pScript->Reload();
				}
				pThisEntity->Reload(true);
			}
		}
	}

	SEntityEvent event;
	event.event = ENTITY_EVENT_RELOAD_SCRIPT;
	gEnv->pEntitySystem->SendEventToAll( event );
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::OnMenuEditAnimationGraph(IAnimatedCharacter* pAnimatedCharacter, EAnimationGraphLayerID graphLayer)
{
	if (pAnimatedCharacter)
	{
		string graphPath = "animations\\graphs\\";
		graphPath += pAnimatedCharacter->GetAnimationGraph(graphLayer)->GetName();

		CWnd* pAnimationGraphWindow = static_cast<CMainFrame*>(AfxGetMainWnd())->OpenPage("Animation Graph", true);

		if (pAnimationGraphWindow && pAnimationGraphWindow->IsKindOf(RUNTIME_CLASS(CAnimationGraphDialog2)))
		{
			CAnimationGraphDialog2* pAnimationGraphDialog = static_cast<CAnimationGraphDialog2*>(pAnimationGraphWindow);
			pAnimationGraphDialog->SetGraph(graphPath);
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::OnContextMenu(CPopupMenuItem* pMenu)
{
	if (!pMenu->Empty())
		pMenu->AddSeparator();

	std::vector<CFlowGraph*> flowgraphs;
	CFlowGraph* pEntityFG = 0;
	FlowGraphHelpers::FindGraphsForEntity(this, flowgraphs, pEntityFG);

	if (GetFlowGraph() == 0)
	{
		pMenu->Add("Create Flow Graph", functor(*this, &CEntityObject::OnMenuCreateFlowGraph));
		pMenu->AddSeparator();
	}

	if (!flowgraphs.empty())
	{
		CPopupMenuItem& fgMenu = pMenu->Add("Flow Graphs");

		std::vector<CFlowGraph*>::const_iterator iter, end;
		for (iter = flowgraphs.begin(), end = flowgraphs.end(); iter != end; ++iter) 
		{
			if (!*iter)
				continue;
			CString name;
			FlowGraphHelpers::GetHumanName(*iter, name);
			if (*iter == pEntityFG)
			{
				name+=" <GraphEntity>";
				
				fgMenu.Add(name, functor(*this, &CEntityObject::OnMenuFlowGraphOpen), *iter);
				if (flowgraphs.size() > 1)
					fgMenu.AddSeparator();
			}
			else
			{
				fgMenu.Add(name, functor(*this, &CEntityObject::OnMenuFlowGraphOpen), *iter);
			}
		}
		pMenu->AddSeparator();
	}

	// TrackView sequences
	std::vector<IAnimNode*> nodeList;
	GetAllRelatedAnimNodes(nodeList);

	if (!nodeList.empty())
	{
		CPopupMenuItem& sequenceMenu = pMenu->Add("Track Views");

		const size_t nodeListCount = nodeList.size();
		for (size_t nodeIndex = 0; nodeIndex < nodeListCount; ++nodeIndex)
		{
			IAnimSequence *pSequence = nodeList[nodeIndex]->GetSequence();
			if (!pSequence)
				continue;
			sequenceMenu.Add(pSequence->GetFullName(), functor(*this, &CEntityObject::OnMenuOpenTrackView), nodeList[nodeIndex]);
		}

		pMenu->AddSeparator();
	}

	// Events
	CEntityScript *pScript = GetScript();
	if (pScript && pScript->GetEventCount()>0)
	{
		CPopupMenuItem& eventMenu = pMenu->Add("Events");

		int eventCount = pScript->GetEventCount();
		for (int i = 0; i < eventCount; ++i)
		{
			CString sourceEvent = pScript->GetEvent(i);
			eventMenu.Add(sourceEvent, functor(*this, &CEntityObject::OnMenuScriptEvent), i);
		}
		pMenu->AddSeparator();
	}

	pMenu->Add("Reload Script", functor(*this, &CEntityObject::OnMenuReloadScripts));
	pMenu->Add("Reload All Scripts", functor(*this, &CEntityObject::OnMenuReloadAllScripts));

	//Quick access to the Animation Graphs used by this entity
	IActor* pActor = (gEnv->pGame && gEnv->pGame->GetIGameFramework()) ? gEnv->pGame->GetIGameFramework()->GetIActorSystem()->GetActor(GetEntityId()) : NULL;

	if(pActor)
	{
		if(IAnimatedCharacter* pAnimatedCharacter = pActor->GetAnimatedCharacter())
		{
			const bool hasLayer0 = (pAnimatedCharacter->GetAnimationGraph(eAnimationGraphLayer_FullBody)!=NULL);
			const bool hasLayer1 = (pAnimatedCharacter->GetAnimationGraph(eAnimationGraphLayer_UpperBody)!=NULL);

			if(hasLayer0 || hasLayer1)
				pMenu->AddSeparator();

			if(hasLayer0)
			{
				pMenu->Add("Edit Fullbody Animation Graph", functor(*this, &CEntityObject::OnMenuEditAnimationGraph), pAnimatedCharacter, eAnimationGraphLayer_FullBody);
			}

			if(hasLayer1)
			{
				pMenu->Add("Edit Upperbody Animation Graph", functor(*this, &CEntityObject::OnMenuEditAnimationGraph), pAnimatedCharacter, eAnimationGraphLayer_UpperBody);
			}
		}	
	}

	__super::OnContextMenu( pMenu );
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::SetFrozen( bool bFrozen )
{
	__super::SetFrozen( bFrozen );
	if (m_pFlowGraph)
		GetIEditor()->GetFlowGraphManager()->SendNotifyEvent( EHG_GRAPH_UPDATE_FROZEN );
}

//////////////////////////////////////////////////////////////////////////
bool CEntityObject::IsScalable()
{
	if (!m_bForceScale)
	{
		return !(m_pClass && (m_pClass->GetFlags() & ENTITY_SCRIPT_ISNOTSCALABLE));
	}
	else
	{
		return true;
	}
}

//////////////////////////////////////////////////////////////////////////
bool CEntityObject::IsRotatable()
{
	return !(m_pClass && (m_pClass->GetFlags() & ENTITY_SCRIPT_ISNOTROTATABLE));
}

//////////////////////////////////////////////////////////////////////////
void CEntityObject::AdjustBoneAttachLink( int boneAttachLinkIndex, QuatT& boneLinkXform )
{
	CEntityLink& link = GetEntityLink(boneAttachLinkIndex);
	CBaseObject *pTargetObject = GetObjectManager()->FindObject(link.targetId);
	if (pTargetObject == NULL)
		return;
	assert(pTargetObject->IsKindOf(RUNTIME_CLASS(CEntityObject)));
	IEntity *pIEntity = static_cast<CEntityObject*>(pTargetObject)->GetIEntity();
	if (pIEntity == NULL)
		return;
	ICharacterInstance *pCharacter = pIEntity->GetCharacter(0);
	if (pCharacter == NULL)
		return;
	ISkeletonPose *pSkeletonPose = pCharacter->GetISkeletonPose();
	if (pSkeletonPose == NULL)
		return;
	// Recalculate the relative transformation to the bone.
	QuatT worldXform(GetWorldTM());
	CString boneName = link.name;
	boneName = boneName.Right(boneName.GetLength() - strlen(BONE_ATTACH_ENTITY_LINK_PREFIX));
	int jointIndex = pSkeletonPose->GetJointIDByName(boneName);
	if (jointIndex == -1)
		return;
	QuatT jointWorldXform = QuatT(pTargetObject->GetWorldTM()) * pSkeletonPose->GetAbsJointByID(jointIndex);
	QuatT relXform = jointWorldXform.GetInverted() * worldXform;
	boneLinkXform = relXform;
	// Update the 'CEntityLink'.
	link.relPos = relXform.t;
	link.relRot = relXform.q;
	// Update the 'IEntityLink'.
	IEntityLink *pLink = GetIEntity()->GetEntityLinks();
	while (pLink)
	{
		if (link.name == pLink->name)
		{
			pLink->relPos = link.relPos;
			pLink->relRot = link.relRot;
			break;
		}
		pLink = pLink->next;
	}
	// Set the relative transformation info in the attachment.
	IAttachmentManager *pAttachmentManager = pCharacter->GetIAttachmentManager();
	CString attachmentName = link.name;
	attachmentName += "-";
	attachmentName += GetIEntity()->GetName();
	IAttachment *pAttachment = pAttachmentManager->GetInterfaceByName(attachmentName);
	assert(pAttachment);
	if (pAttachment == NULL)
		return;
	pAttachment->SetAttRelativeDefault(relXform);
}

//////////////////////////////////////////////////////////////////////////
bool CEntityObject::GetFinalTransformFromBoneAttach(QuatT& xform)
{
	int boneLinkIndex = GetBoneAttachLinkIndex();
	if (boneLinkIndex < 0)
		return false;
	CString linkName = m_links[boneLinkIndex].name;
	// In this case, pos/rotate mean the local transformation for the bone attachment link.
	// So, we should augment the transformation to be proper for the 'SetLocalTM()'.
	IEntityLink *pLink = GetIEntity()->GetEntityLinks();
	while (pLink)
	{
		if (linkName == pLink->name)
		{
			break;
		}
		pLink = pLink->next;
	}
	if (pLink == NULL)
		return false;

	IEntity *pTargetEntity = gEnv->pEntitySystem->GetEntity(pLink->entityId);
	if (pTargetEntity == NULL)
		return false;
	ICharacterInstance *pCharacter = pTargetEntity->GetCharacter(0);
	if (pCharacter == NULL)
		return false;
	ISkeletonPose *pSkeletonPose = pCharacter->GetISkeletonPose();
	if (pSkeletonPose == NULL)
		return false;

	// Get the link transform.
	IAttachmentManager *pAttachmentManager = pCharacter->GetIAttachmentManager();
	string attachmentName = pLink->name;
	attachmentName += "-";
	attachmentName += GetIEntity()->GetName();
	IAttachment *pAttachment = pAttachmentManager->GetInterfaceByName(attachmentName);
	assert(pAttachment);
	if (pAttachment == NULL)
		return false;
	QuatT relXform = pAttachment->GetAttRelativeDefault();

	// Get the target joint transform.
	CString boneName = linkName;
	boneName = boneName.Right(boneName.GetLength() - strlen(BONE_ATTACH_ENTITY_LINK_PREFIX));
	int jointIndex = pSkeletonPose->GetJointIDByName(boneName);
	if (jointIndex == -1)
		return false;
	QuatT jointWorldXform = QuatT(pTargetEntity->GetWorldTM()) * pSkeletonPose->GetAbsJointByID(jointIndex);

	// Concat them.
	xform = jointWorldXform * relXform;
	return true;
}

void CEntityObject::GetAttachedBoneTransform(QuatT& xform)
{
	assert(GetBoneAttachLinkIndex() >= 0);
	CEntityLink& link = GetEntityLink(GetBoneAttachLinkIndex());
	CBaseObject *pTargetObject = GetObjectManager()->FindObject(link.targetId);
	if (pTargetObject == NULL)
		return;
	assert(pTargetObject->IsKindOf(RUNTIME_CLASS(CEntityObject)));
	IEntity *pIEntity = static_cast<CEntityObject*>(pTargetObject)->GetIEntity();
	if (pIEntity == NULL)
		return;
	ICharacterInstance *pCharacter = pIEntity->GetCharacter(0);
	if (pCharacter == NULL)
		return;
	ISkeletonPose *pSkeletonPose = pCharacter->GetISkeletonPose();
	if (pSkeletonPose == NULL)
		return;

	CString boneName = link.name;
	boneName = boneName.Right(boneName.GetLength() - strlen(BONE_ATTACH_ENTITY_LINK_PREFIX));
	int jointIndex = pSkeletonPose->GetJointIDByName(boneName);
	if (jointIndex == -1)
		return;
	xform = QuatT(pTargetObject->GetWorldTM()) * pSkeletonPose->GetAbsJointByID(jointIndex);
}

//////////////////////////////////////////////////////////////////////////
// CSimple Entity.
//////////////////////////////////////////////////////////////////////////
bool CSimpleEntity::Init( IEditor *ie,CBaseObject *prev,const CString &file )
{
	bool bRes = false;
	if (file.IsEmpty())
	{
		bRes = CEntityObject::Init( ie,prev,"" );
	}
	else
	{
		bRes = CEntityObject::Init( ie,prev,"BasicEntity" );
		SetClass( m_entityClass );
		SetGeometryFile( file );
	}

	return bRes;
}

//////////////////////////////////////////////////////////////////////////
void CSimpleEntity::SetGeometryFile( const CString &filename )
{
	if (m_properties)
	{
		IVariable* pModelVar = m_properties->FindVariable( "object_Model" );
		if (pModelVar)
		{
			pModelVar->Set( filename );
			SetModified(false);
		}
	}
}

//////////////////////////////////////////////////////////////////////////
CString CSimpleEntity::GetGeometryFile() const
{
	CString filename;
	if (m_properties)
	{
		IVariable* pModelVar = m_properties->FindVariable( "object_Model" );
		if (pModelVar)
		{
			pModelVar->Get( filename );
		}
	}
	return filename;
}

//////////////////////////////////////////////////////////////////////////
bool CSimpleEntity::ConvertFromObject( CBaseObject *object )
{
	CEntityObject::ConvertFromObject( object );
	if (object->IsKindOf(RUNTIME_CLASS(CBrushObject)))
	{
		CBrushObject *pBrushObject = (CBrushObject*)object;

		IStatObj *prefab = pBrushObject->GetIStatObj();
		if (!prefab)
			return false;

		// Copy entity shadow parameters.
		int rndFlags = pBrushObject->GetRenderFlags();

		// mv_castShadows = (rndFlags & ERF_CASTSHADOWVOLUME) != 0;
		// mv_selfShadowing = (rndFlags & ERF_SELFSHADOW) != 0;
		mv_castShadow = (rndFlags & ERF_CASTSHADOWMAPS) != 0;
    //mv_recvShadow = (rndFlags & ERF_RECVSHADOWMAPS) != 0;    
		mv_outdoor = (rndFlags & ERF_OUTDOORONLY) != 0;    
		//mv_recvShadowMapsASMR = (rndFlags & ERF_RECVSHADOWMAPS_ACTIVE) != 0;
		//mv_castLightmap = (rndFlags & ERF_CASTSHADOWINTORAMMAP) != 0;
		//mv_recvLightmap = (rndFlags & ERF_USERAMMAPS) != 0;
		mv_ratioLOD = pBrushObject->GetRatioLod();
		mv_ratioViewDist = pBrushObject->GetRatioViewDist();
		mv_recvWind = (rndFlags & ERF_RECVWIND) != 0;
		mv_noDecals = (rndFlags & ERF_NO_DECALNODE_DECALS) != 0;

		SetClass( "BasicEntity" );
		SpawnEntity();
		SetGeometryFile( prefab->GetFilePath() );
	}
	return true;
}

//////////////////////////////////////////////////////////////////////////
void CSimpleEntity::BeginEditParams( IEditor *ie,int flags )
{
	CEntityObject::BeginEditParams( ie,flags );

	CString filename = GetGeometryFile();

	if (gSettings.bGeometryBrowserPanel)
	{
		if (!filename.IsEmpty())
		{
			if (!s_treePanel)
			{
				s_treePanel = new CPanelTreeBrowser;
				int flags = CPanelTreeBrowser::NO_DRAGDROP|CPanelTreeBrowser::NO_PREVIEW|CPanelTreeBrowser::SELECT_ONCLICK;
				s_treePanel->Create( functor(*this,&CSimpleEntity::OnFileChange),GetClassDesc()->GetFileSpec(),AfxGetMainWnd(),flags );
			}
			if (s_treePanelId == 0)
				s_treePanelId = AddUIPage( _T("Geometry"),s_treePanel,false );
		}

		if (s_treePanel)
		{
			s_treePanel->SetSelectCallback( functor(*this,&CSimpleEntity::OnFileChange) );
			s_treePanel->SelectFile( filename );
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CSimpleEntity::EndEditParams( IEditor *ie )
{
	if (s_treePanelId != 0)
	{
		RemoveUIPage( s_treePanelId );
		s_treePanelId = 0;
	}

	CEntityObject::EndEditParams( ie );
}

//////////////////////////////////////////////////////////////////////////
void CSimpleEntity::OnFileChange( CString filename )
{
	CUndo undo("Entity Geom Modify");
	StoreUndo( "Entity Geom Modify" );
	SetGeometryFile( filename );
}

//////////////////////////////////////////////////////////////////////////
//! Analyze errors for this object.
void CSimpleEntity::Validate( CErrorReport *report )
{
	CEntityObject::Validate( report );
	
	// Checks if object loaded.
}

//////////////////////////////////////////////////////////////////////////
bool CSimpleEntity::IsSimilarObject( CBaseObject *pObject )
{
	if (pObject->GetClassDesc() == GetClassDesc() && pObject->GetRuntimeClass() == GetRuntimeClass())
	{
		CSimpleEntity *pEntity = (CSimpleEntity*)pObject;
		if (GetGeometryFile() == pEntity->GetGeometryFile())
		{
			return true;
		}
	}
	return false;
}



//////////////////////////////////////////////////////////////////////////
// CGeomEntity
//////////////////////////////////////////////////////////////////////////
bool CGeomEntity::Init( IEditor *ie,CBaseObject *prev,const CString &file )
{
	bool bRes = false;
	if (file.IsEmpty())
	{
		bRes = CEntityObject::Init( ie,prev,"" );
		SetClass( "GeomEntity" );
	}
	else
	{
		bRes = CEntityObject::Init( ie,prev,"GeomEntity" );
		SetClass( "GeomEntity" );
		SetGeometryFile( file );
	}

	return bRes;
}

//////////////////////////////////////////////////////////////////////////
void CGeomEntity::InitVariables()
{
	AddVariable( mv_geometry,"Geometry",functor(*this,&CGeomEntity::OnGeometryFileChange),IVariable::DT_OBJECT );
	__super::InitVariables();
}

//////////////////////////////////////////////////////////////////////////
void CGeomEntity::SetGeometryFile( const CString &filename )
{
	if (filename != mv_geometry)
		mv_geometry = filename;

	OnGeometryFileChange(&mv_geometry);
}

//////////////////////////////////////////////////////////////////////////
void CGeomEntity::OnGeometryFileChange( IVariable *pVar )
{
	if (m_entity)
	{
		if(!m_bVisible)
			m_entity->Hide( false );  // Unhide to clear hidden state
		CString filename = (CString)mv_geometry;
		const char *ext = PathUtil::GetExt(filename);
		if (stricmp(ext,CRY_CHARACTER_FILE_EXT) == 0 || stricmp(ext,CRY_CHARACTER_DEFINITION_FILE_EXT) == 0 || stricmp(ext,CRY_ANIM_GEOMETRY_FILE_EXT) == 0 || stricmp(ext,CRY_CHARACTER_PARTS_FILE_EXT) == 0)
		{
			m_entity->LoadCharacter( 0,filename,IEntity::EF_AUTO_PHYSICALIZE ); // Physicalize geometry.
		}
		else
		{
			m_entity->LoadGeometry( 0,filename,NULL,IEntity::EF_AUTO_PHYSICALIZE ); // Physicalize geometry.
		}
		if(!m_bVisible)
			m_entity->Hide( true ); // Hide if hidden
		CalcBBox();
		InvalidateTM(0);
		OnRenderFlagsChange(0);
		if(s_treePanel)
			s_treePanel->SelectFile( filename );
	}
}

//////////////////////////////////////////////////////////////////////////
void CGeomEntity::SpawnEntity()
{
	__super::SpawnEntity();
	SetGeometryFile( mv_geometry );
}

//////////////////////////////////////////////////////////////////////////
CString CGeomEntity::GetGeometryFile() const
{
	return mv_geometry;
}

//////////////////////////////////////////////////////////////////////////
bool CGeomEntity::ConvertFromObject( CBaseObject *object )
{
	CEntityObject::ConvertFromObject( object );
	if (object->IsKindOf(RUNTIME_CLASS(CBrushObject)))
	{
		CBrushObject *pBrushObject = (CBrushObject*)object;

		IStatObj *prefab = pBrushObject->GetIStatObj();
		if (!prefab)
			return false;

		// Copy entity shadow parameters.
		int rndFlags = pBrushObject->GetRenderFlags();

		//		mv_castShadows = (rndFlags & ERF_CASTSHADOWVOLUME) != 0;
		//mv_selfShadowing = (rndFlags & ERF_SELFSHADOW) != 0;
		mv_castShadow = (rndFlags & ERF_CASTSHADOWMAPS) != 0;
		//mv_recvShadow = (rndFlags & ERF_RECVSHADOWMAPS) != 0;    
		mv_outdoor = (rndFlags & ERF_OUTDOORONLY) != 0;    
		//mv_recvShadowMapsASMR = (rndFlags & ERF_RECVSHADOWMAPS_ACTIVE) != 0;
		//mv_castLightmap = (rndFlags & ERF_CASTSHADOWINTORAMMAP) != 0;
		//mv_recvLightmap = (rndFlags & ERF_USERAMMAPS) != 0;
		mv_ratioLOD = pBrushObject->GetRatioLod();
		mv_ratioViewDist = pBrushObject->GetRatioViewDist();
		mv_recvWind = (rndFlags & ERF_RECVWIND) != 0;
		mv_noDecals = (rndFlags & ERF_NO_DECALNODE_DECALS) != 0;

		SpawnEntity();
		SetGeometryFile( prefab->GetFilePath() );
	}
	else if (object->IsKindOf(RUNTIME_CLASS(CEntityObject)))
	{
		CEntityObject *pObject = (CEntityObject*)object;
		if (!pObject->GetIEntity())
			return false;

		IStatObj *pStatObj = pObject->GetIEntity()->GetStatObj(0);
		if (!pStatObj)
			return false;

		SpawnEntity();
		SetGeometryFile( pStatObj->GetFilePath() );
	}
	return true;
}

//////////////////////////////////////////////////////////////////////////
void CGeomEntity::BeginEditParams( IEditor *ie,int flags )
{
	CEntityObject::BeginEditParams( ie,flags );

	CString filename = GetGeometryFile();

	if (gSettings.bGeometryBrowserPanel)
	{
		if (!filename.IsEmpty())
		{
			if (!s_treePanel)
			{
				s_treePanel = new CPanelTreeBrowser;
				int flags = CPanelTreeBrowser::NO_DRAGDROP|CPanelTreeBrowser::NO_PREVIEW|CPanelTreeBrowser::SELECT_ONCLICK;
				s_treePanel->Create( functor(*this,&CGeomEntity::OnFileChange),GetClassDesc()->GetFileSpec(),AfxGetMainWnd(),flags );
			}
			if (s_treePanelId == 0)
				s_treePanelId = AddUIPage( _T("Geometry"),s_treePanel,false );
		}

		if (s_treePanel)
		{
			s_treePanel->SetSelectCallback( functor(*this,&CGeomEntity::OnFileChange) );
			s_treePanel->SelectFile( filename );
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CGeomEntity::EndEditParams( IEditor *ie )
{
	if (s_treePanelId != 0)
	{
		RemoveUIPage( s_treePanelId );
		s_treePanelId = 0;
	}

	CEntityObject::EndEditParams( ie );
}

//////////////////////////////////////////////////////////////////////////
void CGeomEntity::EnableEditParams(bool bEnable)
{
	CEntityObject::EnableEditParams(bEnable);
	if(s_treePanel)
		s_treePanel->SetEnable(bEnable);
}

//////////////////////////////////////////////////////////////////////////
void CGeomEntity::EnableEditMultiSelParams(bool bEnable, bool bAllOfSameType)
{
	CEntityObject::EnableEditMultiSelParams(bEnable, bAllOfSameType);
}

//////////////////////////////////////////////////////////////////////////
void CGeomEntity::OnFileChange( CString filename )
{
	CUndo undo("Entity Geom Modify");
	StoreUndo( "Entity Geom Modify" );
	SetGeometryFile( filename );
	SW_ON_OBJ_MOD(this);
}

//////////////////////////////////////////////////////////////////////////
//! Analyze errors for this object.
void CGeomEntity::Validate( CErrorReport *report )
{
	CEntityObject::Validate( report );

	// Checks if object loaded.
}

//////////////////////////////////////////////////////////////////////////
bool CGeomEntity::IsSimilarObject( CBaseObject *pObject )
{
	if (pObject->GetClassDesc() == GetClassDesc() && pObject->GetRuntimeClass() == GetRuntimeClass())
	{
		CGeomEntity *pEntity = (CGeomEntity*)pObject;
		if (GetGeometryFile() == pEntity->GetGeometryFile())
		{
			return true;
		}
	}
	return false;
}

//////////////////////////////////////////////////////////////////////////
XmlNodeRef CGeomEntity::Export( const CString &levelPath,XmlNodeRef &xmlNode )
{
	XmlNodeRef node = CEntityObject::Export( levelPath,xmlNode );
	if (node)
	{
		node->setAttr( "Geometry",(CString)mv_geometry );
	}
	return node;
}

//////////////////////////////////////////////////////////////////////////
void CGeomEntity::OnEvent( ObjectEvent event )
{
	__super::OnEvent(event);
	switch (event)
	{
	case EVENT_INGAME:
	case EVENT_OUTOFGAME:
		if (m_entity)
		{
			// Make entity sleep on in/out game events.
			IPhysicalEntity *pe = m_entity->GetPhysics();
			if (pe)
			{
				pe_action_awake aa;
				aa.bAwake = 0;
				pe->Action(&aa);
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CGeomEntity::GetVerticesInWorld(std::vector<Vec3>& vertices) const
{
	vertices.clear();
	if (m_entity == NULL)
		return;
	IStatObj *pStatObj = m_entity->GetStatObj(0);
	if (pStatObj == NULL)
		return;
	IIndexedMesh *pIndexedMesh = pStatObj->GetIndexedMesh(true);
	if (pIndexedMesh == NULL)
		return;

	IIndexedMesh::SMeshDescription meshDesc;
	pIndexedMesh->GetMeshDescription(meshDesc);

	vertices.reserve(meshDesc.m_nVertCount);

	const Matrix34 tm = GetWorldTM();

	if (meshDesc.m_pVerts)
	{
		for(int v=0; v<meshDesc.m_nVertCount; ++v)
		{
			vertices.push_back(tm.TransformPoint(meshDesc.m_pVerts[v])); 
		}
	}
	else
	{
		for(int v=0; v<meshDesc.m_nVertCount; ++v)
		{
			vertices.push_back(tm.TransformPoint(ToVec3(meshDesc.m_pVertsF16[v]))); 
		}
	}
}
